{"openapi":"3.0.3","info":{"title":"TutorLink API","version":"0.1.0","description":"REST endpoints for TutorLink — bookings, verification, payments, subscriptions, and AI tutor matching. Authenticated routes expect a Supabase session cookie from the Next.js app (log in via /login first).","contact":{"name":"TutorLink","email":"hello@tutorlink.pk"}},"servers":[{"url":"/","description":"Current deployment"}],"tags":[{"name":"Bookings","description":"Parent booking requests"},{"name":"Verification","description":"Tutor verification document records"},{"name":"Payments","description":"Safepay one-time checkout and webhooks"},{"name":"Subscriptions","description":"Premium plan checkout"},{"name":"AI","description":"Gemini-powered tutor matching"},{"name":"Monitoring","description":"Health and Sentry verification"}],"components":{"schemas":{"ApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","example":"unauthenticated"},"message":{"type":"string","example":"Log in to continue."}}}}},"CreateBookingRequest":{"type":"object","required":["tutorId","subject","proposedSchedule"],"properties":{"tutorId":{"type":"string","format":"uuid"},"subject":{"type":"string","maxLength":80},"proposedSchedule":{"type":"string","description":"ISO datetime or schedule text"},"notes":{"type":"string","maxLength":500}}},"AiMatchRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string","maxLength":500},"subject":{"type":"string","maxLength":80},"city":{"type":"string","enum":["Lahore","Islamabad","Karachi"]},"maxBudget":{"type":"number","exclusiveMinimum":0}}},"PaymentCheckoutRequest":{"type":"object","required":["bookingId"],"properties":{"bookingId":{"type":"string","format":"uuid"}}},"VerificationSubmitRequest":{"type":"object","required":["docType","storagePath"],"properties":{"docType":{"type":"string","enum":["cnic","character_certificate"]},"storagePath":{"type":"string"}}}},"securitySchemes":{"CookieAuth":{"type":"apiKey","in":"cookie","name":"sb-access-token","description":"Supabase Auth session cookies set by the TutorLink app after login. Use the browser session when calling from Swagger while logged in."}}},"paths":{"/api/bookings":{"get":{"tags":["Bookings"],"summary":"List bookings for the current user","description":"Returns bookings where the caller is the parent or the tutor.","security":[{"CookieAuth":[]}],"responses":{"200":{"description":"Booking list","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object"}}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"post":{"tags":["Bookings"],"summary":"Create a booking request","description":"Parent-only. Free tier is limited to 10 booking requests per calendar month.","security":[{"CookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBookingRequest"}}}},"responses":{"201":{"description":"Booking created"},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not a parent account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Free-tier contact limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/verification":{"post":{"tags":["Verification"],"summary":"Record a verification document upload","description":"After the client uploads a file to the private Storage bucket, this stores the path for admin review.","security":[{"CookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationSubmitRequest"}}}},"responses":{"201":{"description":"Document record created"},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/payments/checkout":{"post":{"tags":["Payments"],"summary":"Start Safepay checkout for an accepted booking","security":[{"CookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentCheckoutRequest"}}}},"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Booking not in accepted state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/payments/webhook":{"post":{"tags":["Payments"],"summary":"Safepay webhook receiver","description":"Signature-verified. Called by Safepay — not for browser use. Updates payment and subscription state.","responses":{"200":{"description":"Event received"},"401":{"description":"Invalid signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/subscriptions/checkout":{"post":{"tags":["Subscriptions"],"summary":"Start Premium subscription checkout","description":"Requires SAFEPAY_PREMIUM_PLAN_ID. Available to parents and tutors.","security":[{"CookieAuth":[]}],"responses":{"200":{"description":"Checkout URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"Subscription checkout unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/ai/match":{"post":{"tags":["AI"],"summary":"AI tutor matching (Gemini)","description":"Parent-only. Queries approved tutors from the database, then asks Gemini to rank and explain. Free tier: 5 matches/month.","security":[{"CookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiMatchRequest"}}}},"responses":{"200":{"description":"Ranked matches with tutor data","content":{"application/json":{"schema":{"type":"object","properties":{"matches":{"type":"array","items":{"type":"object"}},"candidateCount":{"type":"integer"},"isPremium":{"type":"boolean"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Not a parent account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"Rate limited or free-tier limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/ai/study-tips":{"post":{"tags":["AI"],"summary":"AI study tips for a subject (Gemini)","description":"Returns 4 short practical study tips. Authenticated users.","security":[{"CookieAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subject"],"properties":{"subject":{"type":"string"},"gradeLevel":{"type":"string"},"city":{"type":"string","enum":["Lahore","Islamabad","Karachi"]}}}}}},"responses":{"200":{"description":"Tips array","content":{"application/json":{"schema":{"type":"object","properties":{"tips":{"type":"array","items":{"type":"string"}},"subject":{"type":"string"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/health":{"get":{"tags":["Monitoring"],"summary":"Liveness / readiness probe","responses":{"200":{"description":"Service is up","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"service":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"sentryConfigured":{"type":"boolean"},"geminiConfigured":{"type":"boolean"}}}}}}}}},"/api/sentry-example":{"get":{"tags":["Monitoring"],"summary":"Trigger a sample Sentry error","description":"Intentionally throws so you can verify Sentry captures events. Only enabled when SENTRY_ENABLE_SAMPLE_ERROR=true (or in development).","parameters":[{"name":"confirm","in":"query","required":true,"schema":{"type":"string","enum":["1"]},"description":"Must be 1 to avoid accidental triggers"}],"responses":{"400":{"description":"Missing confirm=1","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"Sample errors disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"500":{"description":"Sample error thrown and captured by Sentry"}}}},"/api/openapi":{"get":{"tags":["Monitoring"],"summary":"OpenAPI JSON document","responses":{"200":{"description":"OpenAPI 3.0 specification"}}}}}}