Skip to content

Platform API Referansı

Achidemy platformundaki tüm API'lerin ne için ve nasıl kullanıldığı — REST endpoint'leri, istek/yanıt formatları, yetkilendirme ve örnekler.

Bu sayfa Achidemy platformunun kendi API dokümantasyonudur. Backend ve full-stack geliştiricilerin tüm API’leri ne için kullanacaklarını, hangi parametreleri göndereceklerini ve ne tür yanıt alacaklarını tek yerden görmesini sağlar. Her endpoint için amaç, yetkilendirme, istek formatı, yanıt ve örnek çağrı verilir.


  • Base URL: Uygulama kökü (yerelde http://localhost:5173 veya http://localhost:8787; production’da https://achidemy.net). API path’leri dil prefix’i içermez (/api/...).
  • Kimlik doğrulama: Çoğu endpoint session cookie (Better Auth) ile korunur. İstekte Cookie header’ı otomatik gönderilir; fetch ile çağırırken credentials: "include" kullanın.
  • Hata yanıtları: 400 (geçersiz istek), 401 (giriş gerekli), 403 (yetkisiz), 404 (bulunamadı), 500 (sunucu hatası). Gövde genelde { "error": "mesaj" } veya düz metin.

ÖzellikDeğer
PathPOST /api/graphql (GET desteklenir; GraphiQL/playground)
Dosyaapp/routes/api.graphql.ts
AmaçTüm veri okuma (courses, me, enrollments, certificates vb.) ve mutation’lar (createCourse, addToCart, toggleLessonCompletion vb.) GraphQL üzerinden yapılır.
YetkilendirmeSession cookie; resolver içinde context.user ile rol ve yetki kontrolü.

İstek (POST):

  • Header: Content-Type: application/json
  • Body: { "query": "string", "variables": { ... } }
  • Cookie: Session cookie (credentials: “include”)

Yanıt: 200{ "data": { ... } } veya { "errors": [ { "message": "..." } ] }

Örnek:

Terminal window
curl -X POST "https://achidemy.net/api/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"query { me { id email name role } }"}' \
--cookie "better-auth.session_token=..."

Şema ve tipler: app/graphql/schema.ts — typeDefs + resolvers. Detay için API Referansı ve GraphQL sayfasına bakın.


ÖzellikDeğer
Path/api/auth/* (tüm alt path’ler)
Dosyaapp/routes/api.auth.tsauth.handler(request)
AmaçGiriş, kayıt, çıkış, OAuth (Google), e-posta doğrulama, şifre sıfırlama, session yönetimi.
YetkilendirmePublic (giriş/kayıt); session gerektiren path’ler Better Auth tarafından yönetilir.

Kullanım: Better Auth client (app/lib/auth-client.ts) ile signIn.email, signUp.email, signOut vb. çağrılır. Doğrudan REST çağrısı yerine SDK kullanılır. Path örnekleri: /api/auth/sign-in/email, /api/auth/sign-up/email, /api/auth/sign-out, /api/auth/callback/google. Detay: API Referansı ve GraphQL — Better Auth.


ÖzellikDeğer
PathPOST /api/stripe/webhook
Dosyaapp/routes/api.stripe.webhook.ts
AmaçStripe’dan gelen olayları işlemek: ödeme tamamlandı (checkout.session.completed), abonelik, payment_intent vb. Enrollment oluşturma, earnings kaydı, e-posta bildirimi burada yapılır.
YetkilendirmeStripe-Signature header ile imza doğrulama; cookie/session yok. Sadece Stripe bu endpoint’e istek atar.

İstek:

  • Header: Stripe-Signature: ... (Stripe tarafından eklenir)
  • Body: Raw (text); Stripe event JSON. Doğrulama için body olduğu gibi kullanılır.

Yanıt: 200 — başarı; 400 — imza hatası veya geçersiz body.

Not: Bu endpoint’i uygulama içinden çağırmazsınız; Stripe Dashboard’da webhook URL olarak tanımlanır. Yerelde test için stripe listen --forward-to localhost:8787/api/stripe/webhook kullanılır.


ÖzellikDeğer
PathPOST /api/stripe/connect-onboarding
Dosyaapp/routes/api.stripe.connect-onboarding.ts
AmaçEğitmeni Stripe Connect Express hesabına yönlendirmek. Hesap yoksa oluşturulur; Account Link ile Stripe onboarding sayfasına redirect döner.
YetkilendirmeSession gerekli; user.role === "instructor". Değilse 401/403.

İstek: POST; body boş veya form. Session cookie gerekli.

Yanıt: 302 Redirect — Stripe Account Link URL’ine yönlendirme. Hata: 401 (giriş gerekli), 403 (sadece eğitmen), 500 (STRIPE_SECRET_KEY veya BASE_URL eksik).

Örnek (form submit):

<form action="/api/stripe/connect-onboarding" method="POST">
<button type="submit">Stripe Hesabını Bağla</button>
</form>

ÖzellikDeğer
PathPOST /api/instructor/update-manual-payout
Dosyaapp/routes/api.instructor.update-manual-payout.ts
AmaçTürkiye’deki eğitmenin IBAN veya Payoneer bilgisini güncellemek. users.manualPayoutDetails ve users.payoutMethod = 'bank_transfer' yazılır.
YetkilendirmeSession + user.role === "instructor".

İstek:

  • Method: POST
  • Content-Type: application/x-www-form-urlencoded veya multipart/form-data
  • Body (FormData): details (string) — IBAN veya Payoneer e-posta adresi.

Yanıt: 302 Redirect — Referer veya /tr/instructor/payouts. Hata: 401, 403 (sadece eğitmen), 404 (kullanıcı bulunamadı), 500.

Örnek:

Terminal window
curl -X POST "https://achidemy.net/api/instructor/update-manual-payout" \
-H "Cookie: better-auth.session_token=..." \
-F "details=TR00 0000 0000 0000 0000 0000 00"

ÖzellikDeğer
PathPOST /api/lesson-resource-upload
Dosyaapp/routes/api.lesson-resource-upload.ts
AmaçDerse PDF vb. kaynak dosyası yüklemek; Bunny Storage’a yazılır. Yükleme sonrası dönen fileUrl ile lesson_resources tablosuna kayıt uygulama tarafında yapılabilir.
YetkilendirmeBu endpoint’te session kontrolü yok; çağıran sayfa (eğitmen müfredat sayfası) session ile korunur. İsteğe bağlı olarak action içinde eğitmen ve ders sahipliği kontrolü eklenebilir.

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • lessonId (string) — Ders UUID.
    • file (File) — Yüklenecek dosya.

Yanıt (başarı): 200{ "title": "dosya.pdf", "fileUrl": "https://...", "fileSize": 12345, "fileType": "application/pdf" }
Hata: 400 (lessonId veya file eksik), 500 (Bunny ayarları eksik), 502 (Bunny yükleme hatası).

Ortam değişkenleri: BUNNY_STORAGE_ZONE, BUNNY_STORAGE_KEY, BUNNY_PULL_ZONE_URL.


ÖzellikDeğer
PathPOST /api/lesson/caption-upload
Dosyaapp/routes/api.lesson.caption-upload.ts
AmaçDers videosuna .vtt altyazı yüklemek; Bunny Stream API’ye gönderilir ve lesson_captions tablosuna kayıt eklenir.
YetkilendirmeEğitmen panelinden çağrılır; action içinde session kontrolü yok (sayfa session ile korunur).

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • lessonId (string) — Ders UUID.
    • captionFile (File) — .vtt dosyası.
    • langCode (string) — Dil kodu (örn. tr, en, de).
    • label (string) — Görünen etiket (örn. Türkçe, English).

Yanıt (başarı): 200{ "message": "Altyazı başarıyla yüklendi" }
Hata: 400 (eksik alan), 404 (video bulunamadı), 500 (Bunny/DB hatası veya env eksik). Tüm 500 yanıtları JSON: { "error": "..." }.

Ortam değişkenleri: BUNNY_LIBRARY_ID, BUNNY_API_KEY veya BUNNY_STREAM_API_KEY.

Detay: Video Altyazıları (Bunny Stream).


ÖzellikDeğer
PathPOST /api/lesson/caption-delete
Dosyaapp/routes/api.lesson.caption-delete.ts
AmaçKayıtlı bir altyazıyı hem Bunny Stream’den hem lesson_captions tablosundan silmek.
YetkilendirmeEğitmen panelinden çağrılır; action içinde session kontrolü yok.

İstek:

  • Method: POST
  • Content-Type: multipart/form-data veya application/x-www-form-urlencoded
  • Body (FormData): captionId (string) — lesson_captions kaydının UUID’si.

Yanıt (başarı): 200{ "success": true }
Hata: 400 (captionId eksik), 404 (kayıt veya video bulunamadı), 500 (Bunny/DB veya env eksik).

Ortam değişkenleri: BUNNY_LIBRARY_ID, BUNNY_API_KEY veya BUNNY_STREAM_API_KEY.


ÖzellikDeğer
PathPOST /api/quiz-resource-upload
Dosyaapp/routes/api.quiz-resource-upload.ts
AmaçQuiz’e PDF vb. kaynak dosyası yüklemek; Bunny Storage’a yazılır.
YetkilendirmeEğitmen panelinden çağrılır; sayfa session ile korunur.

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • quizId (string) — Quiz UUID.
    • file (File) — Yüklenecek dosya.

Yanıt (başarı): 200{ "title": "dosya.pdf", "fileUrl": "https://...", "fileSize": 12345, "fileType": "application/pdf" }
Hata: 400 (quizId veya file eksik), 500 (Bunny ayarları eksik).


ÖzellikDeğer
PathPOST /api/exercise-resource-upload
Dosyaapp/routes/api.exercise-resource-upload.ts
AmaçCoding exercise’e PDF vb. kaynak dosyası yüklemek; Bunny Storage’a yazılır.
YetkilendirmeEğitmen panelinden çağrılır; sayfa session ile korunur.

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • exerciseId (string) — Coding exercise UUID.
    • file (File) — Yüklenecek dosya.

Yanıt (başarı): 200{ "title": "dosya.pdf", "fileUrl": "https://...", "fileSize": 12345, "fileType": "application/pdf" }
Hata: 400 (exerciseId veya file eksik), 500 (Bunny ayarları eksik).


6.5 Ders Videosu İçin Direct Upload Bileti (Bunny TUS)

Section titled “6.5 Ders Videosu İçin Direct Upload Bileti (Bunny TUS)”
ÖzellikDeğer
PathPOST /api/bunny/create-upload
Dosyaapp/routes/api.bunny.create-upload.ts
AmaçBunny Stream üzerinde yeni bir video kaydı (placeholder) oluşturmak ve TUS (resumable upload) protokolü ile doğrudan CDN’e yükleme için gerekli imzalı bileti üretmek. Frontend, bu endpoint’ten dönen değerlerle tus-js-client aracılığıyla büyük boyutlu videoları (ör. 4K, 2GB) doğrudan Bunny’ye yükler.
YetkilendirmeSadece eğitmenler kullanabilir. Session gerekli; user.role === "instructor" kontrolü yapılır, değilse 401 Unauthorized döner.

İstek:

  • Method: POST
  • Content-Type: application/json
  • Body:
{
"title": "Ders Başlığı (opsiyonel)"
}

Yanıt (başarı): 200

{
"videoId": "bunny-guid",
"libraryId": "605053",
"signature": "hex-sha256-signature",
"expirationTime": 1738060000
}
  • videoId: Bunny Stream tarafında oluşturulan video GUID’i (dersin bunnyVideoId alanında saklanır).
  • libraryId: BUNNY_LIBRARY_ID değeri.
  • signature: TUS upload için Bunny’nin beklediği SHA-256 tabanlı imza (AuthorizationSignature).
  • expirationTime: İmzanın geçerli olduğu Unix timestamp (saniye).

Hata:

  • 401 — Kullanıcı yok veya rolü instructor değil.
  • 500BUNNY_LIBRARY_ID veya BUNNY_STREAM_API_KEY / BUNNY_API_KEY eksik ya da Bunny video kaydı oluşturulamadı. Gövde her zaman JSON’dur:
{ "error": "Bunny.net yapılandırması eksik. Lütfen BUNNY_LIBRARY_ID ve BUNNY_STREAM_API_KEY (veya BUNNY_API_KEY) env değişkenlerini tanımlayın." }

Ortam değişkenleri:

  • BUNNY_LIBRARY_ID
  • BUNNY_STREAM_API_KEY veya BUNNY_API_KEY (kodda fallback olarak okunur)

Kullanıldığı yer:

  • DirectVideoUploader bileşeni (app/components/instructor/DirectVideoUploader.tsx), müfredat sayfasında (instructor.course.$slug.manage.curriculum.tsx) derse video yükleme akışında bu endpoint’i çağırır ve dönen videoId, libraryId, signature, expirationTime değerleriyle tus-js-client üzerinden doğrudan Bunny TUS endpoint’ine upload yapar.

Not: Bu endpoint sadece bilet ve imza üretir; video binary verisi backend’e hiç uğramaz. Böylece Cloudflare Workers tarafında yük hafif tutulur, çok büyük videolar için bile ölçeklenebilir ve kesintiye dayanıklı bir upload deneyimi sağlanır.


ÖzellikDeğer
PathPOST /api/chat-image-upload
Dosyaapp/routes/api.chat-image-upload.ts
AmaçMesajda gönderilecek görseli yüklemek; Bunny Storage chat-media/ altına yazılır. Dönen URL mesaj içinde kullanılır.
YetkilendirmeBu endpoint’te session kontrolü yok; mesaj sayfası session ile korunur. İsteğe bağlı action içinde session kontrolü eklenebilir.

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData): file (File) — Görsel; tipler: image/jpeg, image/png, image/gif, image/webp.

Yanıt (başarı): 200{ "url": "https://pullzone.../chat-media/xxx.jpg" }
Hata: 400 (dosya yok veya tip uygun değil), 500 (Bunny ayarları eksik), 502 (yükleme hatası).


ÖzellikDeğer
PathGET /api/certificate/:id/download veya GET /api/certificates/:id/download
Dosyaapp/routes/api.certificate.$id.download.ts
AmaçKullanıcının sertifika kaydına ait PDF’i indirmek. Sertifika userId ile eşleşmeli.
YetkilendirmeSession gerekli; sertifika sahibi (certificates.userId === session.user.id) olmalı.

İstek: GET; path parametresi id — sertifika UUID.

Yanıt (başarı): 200Content-Type: application/pdf, Content-Disposition: attachment; filename="certificate-xxx.pdf". Gövde PDF stream.
Hata: 401 (giriş gerekli), 404 (sertifika bulunamadı veya yetkisiz), 500.


ÖzellikDeğer
PathGET /api/invoice/:enrollmentId/download
Dosyaapp/routes/api.invoice.$enrollmentId.download.ts
AmaçKullanıcının bir enrollment’ına ait fatura PDF’ini indirmek. Aynı checkout session’daki tüm kalemler tek faturada gösterilir.
YetkilendirmeSession gerekli; enrollment sahibi (enrollments.userId === session.user.id) olmalı.

İstek: GET; path parametresi enrollmentId — enrollment UUID.

Yanıt (başarı): 200Content-Type: application/pdf, Content-Disposition: attachment. Gövde PDF.
Hata: 400 (enrollmentId eksik), 401 (giriş gerekli), 404 (enrollment bulunamadı veya yetkisiz), 500.


ÖzellikDeğer
PathGET /api/admin/activities
Dosyaapp/routes/api.admin.activities.ts
AmaçAdmin “Tüm bildirimler” modalı için ödeme talepleri ve kurs onayları gibi aktiviteleri JSON olarak döndürmek (en fazla 500 kayıt).
YetkilendirmeSession + user.role === "admin".

İstek: GET; session cookie gerekli.

Yanıt (başarı): 200{ "activities": [ { "id", "type", "title", "createdAt", ... } ] }
Hata: 401 (giriş gerekli), 403 (sadece admin), 500.

Örnek:

Terminal window
curl "https://achidemy.net/api/admin/activities" \
-H "Cookie: better-auth.session_token=..."

11. Video İzlenme Telemetrisi (Heartbeat)

Section titled “11. Video İzlenme Telemetrisi (Heartbeat)”
ÖzellikDeğer
PathPOST /api/video-telemetry
Dosyaapp/routes/api.video-telemetry.ts
AmaçVideo izlenme telemetrisini 15 saniyelik bloklar halinde kaydetmek. Sunucu watch_time_logs insert eder; daily_activities artırır; abonelik izlenmelerinde subscription_watch_logs artırır.
YetkilendirmeSession zorunlu (auth.api.getSession). userId body’den alınmaz.

Enterprise Anti‑Cheat (Payout Koruması):

  • Self‑Watch Ban: Eğitmen kendi kursunu izlerse payout’a dahil edilmez (payout logları atlanır; daily_activities yine güncellenir).
  • Human Daily Cap: Kullanıcı başına günlük maksimum “paid” süre 28.800 sn (8 saat).
  • Lesson 3x Cap: Ay içinde aynı ders için maksimum “paid” süre lessons.duration * 3.

İstek:

  • Method: POST
  • Content-Type: application/json
  • Body: { "courseId": "uuid", "lessonId": "uuid", "durationSeconds": number, "timestamp"?: number }
    • durationSeconds > 0 olmalı ve anti-cheat için tek istekte <= 25 olmalıdır (pratikte 15).

Yanıt (başarı): 200{ "success": true }
Hata: 400 (Invalid payload — eksik/geçersiz alan), 401 (Unauthorized), 500.

Örnek:

Terminal window
curl -X POST "https://achidemy.net/api/video-telemetry" \
-H "Content-Type: application/json" \
-H "Cookie: better-auth.session_token=..." \
-d '{"courseId":"...","lessonId":"...","durationSeconds":15,"timestamp":1710000000000}'

11.1 Legacy: Video İzleme İlerlemesi (Deprecated)

Section titled “11.1 Legacy: Video İzleme İlerlemesi (Deprecated)”
ÖzellikDeğer
PathPOST /api/update-progress
Dosyaapp/routes/api.update-progress.ts
DurumDeprecated (geriye dönük uyumluluk). Yeni akış için POST /api/video-telemetry kullanın.

12. Kariyer CV PDF Yükleme (Bunny Storage)

Section titled “12. Kariyer CV PDF Yükleme (Bunny Storage)”
ÖzellikDeğer
PathPOST /api/career-cv-upload
Dosyaapp/routes/api.career-cv-upload.ts
AmaçKariyer başvuru formunda CV PDF’i yüklemek; Bunny Storage’a yazıp url döndürmek.
YetkilendirmeSession yok; public erişim (FormData: jobId, file).

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • jobId (string) — ilan id’si (UUID)
    • file (File) — sadece application/pdf

Yanıt (başarı): 200{ "url": "https://pullzone.../careers/job/.../cv.pdf" }

Hata: 400 (jobId/file/yetersiz format), 500 (Bunny config eksik), 502 (upload başarısız).


13. Admin Kariyer CV İndir (Admin-only proxy)

Section titled “13. Admin Kariyer CV İndir (Admin-only proxy)”
ÖzellikDeğer
PathGET /api/admin/career-cv/:applicationId/download
Dosyaapp/routes/api.admin.career-cv.$applicationId.download.ts
AmaçAdmin panelden başvurunun CV PDF’ini güvenli şekilde indirmek. Bunny Pull Zone 403/hotlink sorunlarını aşmak için Storage API ile proxy yapılır.
YetkilendirmeSession gerekli; user.role === "admin".

İstek: GET; path parametresi applicationIdcareer_applications.id (UUID).

Yanıt (başarı): 200Content-Type: application/pdf ve Content-Disposition: attachment; filename="..." (stream).

Hata: 401, 404, 500.


14. Blog Kapak Görseli Yükleme (Bunny Storage)

Section titled “14. Blog Kapak Görseli Yükleme (Bunny Storage)”
ÖzellikDeğer
PathPOST /api/blog-image-upload
Dosyaapp/routes/api.blog-image-upload.ts
AmaçAdmin blog panelinde seçilen görseli Bunny Storage’a yüklemek ve public URL döndürmek.
YetkilendirmeSession yok; public erişim (FormData: file, slug).

İstek:

  • Method: POST
  • Content-Type: multipart/form-data
  • Body (FormData):
    • slug (string) — blog yazısı slug
    • file (File) — sadece image/*

Bunny path: blog/{safeSlug}/image/{encodedName}

Yanıt (başarı): 200{ "url": "https://pullzone.../blog/.../image/xxx.jpg" }

Hata: 400 (file yok/format uygunsuz), 500 (Bunny config veya upload hatası).


EndpointMethodAmaçYetki
/api/graphqlPOSTGraphQL sorgu/mutationSession (resolver bazlı)
/api/auth/**Giriş, kayıt, OAuth, sessionPublic / Better Auth
/api/stripe/webhookPOSTStripe olayları (ödeme, abonelik)Stripe-Signature
/api/stripe/connect-onboardingPOSTStripe Connect onboarding redirectInstructor
/api/instructor/update-manual-payoutPOSTTR manuel ödeme bilgisi (IBAN/Payoneer)Instructor
/api/lesson-resource-uploadPOSTDers kaynağı yükleme (Bunny)FormData: lessonId, file
/api/quiz-resource-uploadPOSTQuiz kaynağı yükleme (Bunny)FormData: quizId, file
/api/exercise-resource-uploadPOSTKod egzersizi kaynağı yükleme (Bunny)FormData: exerciseId, file
/api/lesson/caption-uploadPOSTVideo altyazısı (.vtt) yükleme (Bunny Stream)FormData: lessonId, captionFile, langCode, label
/api/lesson/caption-deletePOSTVideo altyazısı silme (Bunny Stream)FormData: captionId
/api/chat-image-uploadPOSTSohbet görseli yükleme (Bunny)FormData: file
/api/blog-image-uploadPOSTBlog kapak görseli yükleme (Bunny Storage)FormData: file, slug
/api/career-cv-uploadPOSTKariyer CV PDF yükleme (Bunny Storage)FormData: jobId, file
/api/admin/career-cv/:applicationId/downloadGETAdmin-only CV indirme (proxy)Admin + PDF stream
/api/certificate/:id/downloadGETSertifika PDF indirmeSession, sertifika sahibi
/api/certificates/:id/downloadGETAynı (geriye dönük URL)Aynı
/api/invoice/:enrollmentId/downloadGETFatura PDF indirmeSession, enrollment sahibi
/api/admin/activitiesGETAdmin aktivite listesi (JSON)Admin
/api/video-telemetryPOSTVideo izlenme telemetrisi (15 sn heartbeat)Session
/api/update-progressPOSTVideo izleme süresi kaydıBody: userId, lessonId, …

GraphQL — Önemli Sorgular ve Mutation’lar

Section titled “GraphQL — Önemli Sorgular ve Mutation’lar”

Şema tam listesi app/graphql/schema.ts içindedir. Sık kullanılanlar:

  • Query: me, courses, courseBySlug, myCertificates, conversationMessages, unreadMessagesCount, adminCourses, adminPayoutRequests vb.
  • Mutation: createCourse, updateCourse, toggleLessonCompletion, recordLearningActivity, requestPayout, createStripeCheckoutSession, addToCart, createReview, updatePayoutSettings vb.

İstemci örneği: app/lib/graphql-client.tsfetch("/api/graphql", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, variables }), credentials: "include" }).