Source: specs/02-api.mdEdit this page

Spec 02 — API Contracts (NestJS, REST /api/v1)

Conventions: JSON; DTO validation (class-validator), strict — unknown properties are rejected, not stripped; request bodies are size-capped; errors { statusCode, code, message, messageAr }; pagination ?page=1&limit=20{ items, total, page, limit }; auth via httpOnly cookies nutqi_at / nutqi_rt; guard decorators @Roles(...); Swagger annotated and served only when explicitly enabled in production.

Read paths return flat rows (D-26): names resolved to a single string, calendar dates as YYYY-MM-DD, avatars as ready-to-use URLs, counters computed server-side, relations dropped — the database layout never leaks to a client.

Rate limiting is per IP, with a much tighter budget on /auth/login and the OTP routes; it complements (does not replace) the account lockout of G15. JWT_ACCESS_SECRET and CORS_ORIGIN are required in production — the app refuses to boot rather than fall back to an insecure default.

health module#

  • GET /health (public, unauthenticated) → liveness plus a real database check. Deliberately free of version numbers, connection strings and error text; consumed by the reverse proxy and the container healthcheck.

auth module#

  • POST /auth/register { role: PERSONAL_PATIENT|GUARDIAN|SPECIALIST|CENTER, firstName, lastName, email, phone, password, gender?, birthDate?, center? {nameAr, licenseNumber?, governorate?, city?, branchName?} } → creates user (status PENDING_VERIFICATION) (+Center+first Branch when role CENTER per G03); sends email+phone OTP (console provider in dev). 409 on dup email/phone.
  • POST /auth/verify-otp { target, code, purpose: "verify" | "reset" } → on verify: marks verified, status → ACTIVE (patient/guardian) or PENDING_ACTIVATION (specialist/center); sets cookies. On reset: returns one-time resetToken.
  • POST /auth/resend-otp { target, channel? } — throttled 60s (matches UI countdown).
  • POST /auth/login { email, password } → cookies; wrong password increments counter; locked → 423 with lockedUntil (G15).
  • POST /auth/forgot { email, phone } → OTP (purpose reset).
  • POST /auth/reset-password { resetToken, password }.
  • POST /auth/refresh, POST /auth/logout.
  • GET /auth/me → user + role-specific profile summary + activation completeness % (drives the gate ring).
  • PATCH /auth/password { current, next } (settings).

users module#

  • PATCH /users/me (names ar/en, nationalId, DOB, gender, avatar, locale, address fields).
  • PUT /users/me/languages [{language, level}] (spoken languages, G13-b).
  • GET/PUT /users/me/notification-channels [{channel, target, enabled}]; POST /users/me/notification-channels/:channel/verify (OTP flow).

patients module (guardian portal)#

  • GET /patients (mine) / POST /patients (add child, G09) / GET|PATCH /patients/:id.
  • POST /patients/:id/documents (multipart, kind DOC|VIDEO) / GET /patients/:id/documents / DELETE …/:docId.
  • Diagnosis: GET /forms/diagnosis-template → system template; POST /patients/:id/diagnosis-response (creates/updates response via answers API below).

specialists module#

  • GET /specialists — public directory (G12): filters q, governorate, specialty, sessionType, priceMin/Max, rating; only ACTIVE.
  • GET /specialists/:id/profile → overview payload (stats: bookings timeline, type distribution, top programs — computed), data (certificates, trainings, videos), clinics, reviews summary.
  • GET /specialists/:id/schedule?weekStart= → sessions grid + prices; GET /specialists/:id/slots?date=&clinicId?= → available 30-min slots (work hours − bookings − days off) (D-09).
  • Me-scoped: GET/PUT /specialists/me/profile, POST/PATCH/DELETE /specialists/me/work-info, …/certificates, …/videos, …/clinics, PUT /specialists/me/schedule (weekday blocks), POST /specialists/me/days-off.
  • PUT /specialists/me/booking-settings { availableForWork, acceptsOnline, acceptsOffline, acceptsConsultation }.
  • GET/POST/DELETE /specialists/me/blocklist.
  • GET /specialists/me/stats?period=all|year|month|week|day → { dailyAvgCases, totalCases, earningsCents, pending: bool }.

bookings module#

  • POST /bookings { patientId, specialistId, clinicId?|type ONLINE, sessionType, date, startTime } → PENDING; validates slot free, specialist active+available, not blocked; price resolved server-side. 409 slot taken.
  • GET /bookings role-scoped list w/ filters (status, type, q, dateRange) + ?export=csv.
  • PATCH /bookings/:id/status { status, meetingUrl?, newDate?/newTime? for POSTPONED } — allowed transitions per state machine (spec docs-content/02 §5); guardian may CANCEL own PENDING/WAITING.
  • GET /bookings/upcoming → next booking for banner (E10).

sessions module#

  • POST /sessions (from booking or special case) / PATCH /sessions/:id { progressPercent, evaluation, notes }.
  • GET /patients/:id/sessions, GET /special-cases/:id/sessions.

special-cases module (specialist)#

  • CRUD /special-cases; attachments /special-cases/:id/attachments (phase BEFORE|AFTER); notes CRUD (soft delete + undo window G18).

forms module (plans & assessments engine)#

  • Templates: GET/POST /forms/templates (mine), GET/PATCH/DELETE /forms/templates/:id (+pages/questions nested payload, versioned).
  • Assign: POST /forms/assignments { templateId, patientId|specialCaseId, dueDate? } → notification to guardian.
  • GET /forms/assignments?role=guardian|specialist&status= → cards data (Not answered / Answered).
  • Answer: GET /forms/assignments/:id/response (or create), PUT /forms/responses/:id/answers/:questionId { value } — autosave upsert (E01), POST /forms/responses/:id/submit → validates required → assignment ANSWERED.
  • Results: GET /forms/assignments/:id/result (Q + A read view).

reviews module#

  • POST /reviews { specialistId, bookingId?, stars, text, kind } (author must have DONE booking with specialist for SESSION kind).
  • GET /specialists/:id/reviews?kind=&period=; PATCH /reviews/:id/like, PATCH /reviews/:id/reply (specialist), POST /reviews/:id/report (G17).

payments module#

  • GET /payments/mine (guardian ledger, G01) → rows + totals.
  • POST /bookings/:id/payment { method, status } (secretary/specialist records; creates wallet EARNING txn on PAID).
  • GET /wallet (specialist) → { balanceCents, withdrawnCents }; GET /wallet/transactions.
  • POST /wallet/withdrawals { amountCents, method, target } (G19); GET /wallet/withdrawals.

centers module#

  • GET/PATCH /centers/me (owner/manager); branches CRUD /centers/me/branches.
  • Staff: GET/POST /centers/me/staff (create staff user w/ role+branch+salary), PATCH/DELETE /centers/me/staff/:id.
  • Patients of center: GET /centers/me/patients (+profile passthroughs).

hr module (center)#

  • POST /hr/attendance/clock-in|clock-out (self, CENTER_SPECIALIST/SECRETARY) — branchId optional (G20).
  • GET /hr/attendance?staffId?&range (owner/manager see all; staff self).
  • CRUD-ish: /hr/absences, /hr/overtime, /hr/penalties (with deductionCents), owner/manager only.
  • Requests: POST /hr/requests (staff), GET /hr/requests (scoped), PATCH /hr/requests/:id/decision { status: APPROVED|REJECTED } (owner/manager) → notification.

jobs module#

  • Center: GET/POST /jobs/postings, PATCH /jobs/postings/:id (draft→published→closed), applications: GET /jobs/postings/:id/applications, PATCH /jobs/applications/:id/decision → on ACCEPTED offers CenterStaff creation payload.
  • Specialist: GET /jobs/market (published, filters, G08), POST /jobs/postings/:id/apply, GET /jobs/applications/mine.

notifications module#

  • GET /notifications?unread=, PATCH /notifications/:id/read, PATCH /notifications/read-all.
  • GET /notifications/stream — SSE (E13).
  • Emitter service used by other modules; dispatches IN_APP always + enabled verified channels (email via console/dev SMTP; WhatsApp/Telegram = interface stubs logging payloads, D-17).

files module#

  • POST /files multipart (auth) → StoredFile; GET /files/:id (authz by ownership/linkage); size/type limits (images 5MB, docs 10MB, video 100MB).

admin module (API-only v1)#

  • GET /admin/activations (pending specialists/centers), PATCH /admin/activations/:userId { approve|reject }.
  • GET /admin/withdrawals, PATCH /admin/withdrawals/:id { TRANSFERRED|REJECTED }.
  • GET /admin/reported-reviews.

Events → notifications (minimum)#

booking.created (→specialist/secretary), booking.status_changed (→guardian), assignment.created (→guardian), assignment.answered (→specialist), application.decided (→specialist), hr.request.decided (→staff), withdrawal.processed (→specialist), activation.decided (→user).