API Documentation

Tutor is a Next.js 16 (App Router) application. There is no separate API server — every endpoint is a route handler living under src/app/api/. They run on the same host as the storefront and admin panel, so the base URL is simply your site origin. It is a lesson booking API: search lessons, read tutor availability, price a quote, and create a booking. All money is in minor units (cents).

Base URL (local): http://localhost:3000 Base URL (production): https://your-domain.com

There are several families of endpoints, each authenticated differently:

Family Path prefix Auth
Public / storefront /api/v1/*, /api/license/status Open (no auth)
Customer / host auth /api/v1/portal/auth/* Session cookie member_session (JWT)
Customer portal /api/v1/customer/* Customer session
Host portal /api/v1/host/* Host session (a customer who is an active host)
Influencer portal /api/v1/influencer/* Influencer session (influencer_session)
Admin /api/v1/admin/* Session cookie admin_token (JWT) + RBAC permission
Install wizard /api/install/* First-run only, gated by the installer

The public/mobile API is the non-admin surface under /api/v1. Admin endpoints are for the back office only and are excluded from the mobile API reference.


Authentication

Tutor uses cookie-based JWT sessions for every first-party surface. Each cookie is also accepted as an Authorization: Bearer <jwt> header for mobile clients (bearer is checked before the cookie). There are four sessions:

Session Cookie Sign-in endpoint
Admin admin_token POST /api/v1/admin/auth/login
Customer member_session POST /api/v1/portal/auth/login
Host (tutor) member_session POST /api/v1/portal/auth/login
Influencer influencer_session POST /api/v1/influencer/auth/login

Admin session (admin_token)

Admins sign in at POST /api/v1/admin/auth/login. On success the server sets an HTTP-only admin_token cookie holding a signed JWT. Every /api/v1/admin/* route reads that cookie, and most write routes additionally enforce a permission (RBAC) via requirePermission(...) — there is no global middleware, the check is per-route.

HTTP
POST /api/v1/admin/auth/login
Content-Type: application/json

{ "email": "admin@example.com", "password": "••••••••" }

Customer / host session (member_session)

Customers register and verify via email OTP, then receive a member_session cookie (JWT). A host is simply a signed-in customer who also owns an active hosts row, so hosts use the same cookie. /api/v1/portal/auth/* handles the account lifecycle:

Endpoint Purpose
POST /api/v1/portal/auth/register Create an account + send email OTP
POST /api/v1/portal/auth/verify-otp Verify OTP → set session cookie
POST /api/v1/portal/auth/login Sign in → set session cookie
POST /api/v1/portal/auth/logout Clear the session
POST /api/v1/portal/auth/forgot · /reset Password reset via OTP
POST /api/v1/portal/auth/check-email Check if an email is registered
GET /api/v1/portal/auth/social/... Start a social sign-in flow
GET /api/v1/portal/me Current customer profile

Interactive API reference (OpenAPI)

Tutor ships an auto-generated OpenAPI 3 document rendered with Scalar. The spec is generated from the route handlers themselves (scripts/gen-openapi.ts, run via npm run gen:openapi), so a mobile-app developer always has an accurate, current API surface.

Resource URL Notes
Scalar reference /api/docs/v1 Two-panel Scalar UI over the spec (admin-gated)
Raw OpenAPI JSON /api/openapi.json Serves the generated spec.generated.json (admin-gated)

Both are admin-gated: without a valid admin_token session the reference page redirects to /admin/login and the spec returns 401, so the spec can't be scraped publicly. Both send noindex, nofollow.

The generator scans every src/app/api/**/route.ts, derives each path ([id]{id}, route groups dropped), the exported HTTP methods, and the auth requirement (from which helper the file calls — getSession→Admin, getCustomerId→Customer, getHostId→Host, getInfluencerId→Influencer). It groups endpoints into mobile-app surfaces — Auth · Home & content · Listings & search · Bookings & checkout · Reviews · Forms · Customer app · Host app · Influencer app — and excludes internal areas (admin, install, license, plugins, ext, cron, and the chat/analytics routes) so the reference stays focused on the mobile/public surface.

Terminal
# Regenerate after adding/renaming routes
npm run gen:openapi

Endpoint reference

The tables below list real route handlers under src/app/api/v1/. List endpoints accept query params; :slug / :id are path params. Paginated lists return { data, meta }; detail endpoints return { data } (or the resource directly); money is always in cents.

Listings & search (lessons)

Method Path Purpose
GET /api/v1/listings Browse/search published lessons (paginated)
GET /api/v1/listings/:slug Lesson detail
GET /api/v1/listings/availability?listing_id=&from=&to= Bookable lesson times in a window + seats_left
POST /api/v1/listings/quote Price a session (see below)
GET /api/v1/listings/:slug/reviews · POST List / submit lesson reviews
POST /api/v1/listings/report Report an issue with a lesson
GET /api/v1/amenities · /api/v1/extras "What's included" + paid add-ons

GET /api/v1/listings supports page, per_page (≤ 50, default 12), sort (recommended · price_asc · price_desc · rating · reviews), q, city, category, amenities, guests (seats needed), min_rating, instant, price_min, price_max, lang. It returns:

JSON
{ "data": [ /* lesson cards */ ],
  "meta": { "total": 42, "page": 1, "per_page": 12, "total_pages": 4, "sort": "recommended" } }

Quote prices a single lesson time server-side (the client never sends an amount):

HTTP
POST /api/v1/listings/quote
Content-Type: application/json

{ "session_id": 128, "seats": 2, "booking_mode": "per_person" }

booking_mode is per_person (charge per seat) or private (a private buyout of the lesson time). The response includes availability, seat count, subtotal, extras, and totals — all in cents.

Bookings & checkout

Method Path Purpose
POST /api/v1/bookings Create a pending booking (re-quotes server-side)
POST /api/v1/bookings/checkout Create the booking and start payment
POST /api/v1/bookings/pay Start / continue payment for a booking
POST /api/v1/bookings/verify Verify a gateway payment
GET /api/v1/bookings/payment-methods Enabled payment methods
* /api/v1/bookings/paypal PayPal REST create/capture callbacks
POST /api/v1/coupons/validate Validate a coupon

A booking is keyed on a session_id + seats + booking_mode plus guest details. Seats are claimed atomically on creation (seats_total - seats_booked >= seats), so overselling is impossible; a race returns 409.

HTTP
POST /api/v1/bookings/checkout
Content-Type: application/json

{
  "session_id": 128,
  "seats": 2,
  "booking_mode": "per_person",
  "guest_name": "Sofia Rossi",
  "guest_email": "sofia@example.com",
  "guest_phone": "+39 …",
  "guest_country": "IT",
  "extras": [ { "id": 4, "qty": 2 } ],
  "coupon_code": "WELCOME10",
  "special_requests": "Focus on algebra basics",
  "pay_option": "deposit",
  "provider": "paypal"
}
JSON
{ "reference": "Tutor-8FQ2", "public_id": "bkg_…",
  "total": 12000, "deposit": 4000, "currency": "EUR", "amount": 4000, "outcome": { /* gateway */ } }

pay_option is deposit or full; the server charges the deposit only when the guest chose it and a deposit exists (0 < deposit < total), otherwise the full amount. All figures are cents.

Reviews & forms

Method Path Purpose
GET /api/v1/reviews Public reviews feed
POST /api/v1/forms Contact / enquiry / newsletter submissions

Content & reference data (public)

Method Path Purpose
GET /api/v1/brand · /pages/:slug · /blogs · /faqs White-label branding + CMS
GET /api/v1/gallery · /testimonials · /hero-sliders · /destinations Home & content blocks
GET /api/v1/countries · /currencies · /languages · /locations Geo / i18n reference
GET /api/v1/i18n UI translation catalogue

Portal endpoints (customer / host / influencer)

Each signed-in portal has its own resource tree, scoped to the current user:

Prefix Representative resources
/api/v1/customer/* dashboard, bookings, payments, wishlist, reviews, notifications, support, documents, profile
/api/v1/host/* dashboard, listings, sessions, calendar, reservations, earnings, payouts, plans, subscription, insights
/api/v1/influencer/* dashboard, referral-links, bookings, commissions, payouts, analytics, products

License

Method Path Purpose
GET /api/license/status Public license status (for the frontend banner)
GET/POST /api/v1/admin/license Admin: view / activate the domain license

Admin (excluded from the mobile API)

Admin resources (/api/v1/admin/*) manage listings, bookings, hosts, customers, extras, coupons, refunds (the approval queue), reviews, blogs, FAQs, gallery, hero slider, pages, menus, theme builder, master-data, partners, testimonials, issues, leads, settings, and license. They require an admin_token session plus the relevant RBAC permission and are not part of the mobile API reference.


Error responses

Routes return JSON { "error": "..." } with the appropriate HTTP status (via err() / serverErr()):

Code Meaning
400 Bad request / validation error
401 Not signed in (missing/invalid session)
403 Forbidden — missing RBAC permission
404 Resource not found
409 Conflict — the seats were just taken (atomic claim failed)
500 Server error
JSON
{ "error": "Those seats have just been taken." }

© CreativeCape Solutions · creative-cape.com · support@creative-cape.com