Files
baya-monorepo/archive/build-chain/working-context/backend/handoff/after-backend-phase-15.md
T
2026-08-02 18:48:32 +03:30

4.5 KiB

Handoff — after backend phase 15 (Messaging, partner centers & admin backoffice)

This is the final backend phase. The backend chain is complete. Every domain the admin backoffice acts on now exists and is wired together.

What is now live (frontend can build against it)

Tickets — the post-booking channel (f14 messaging)

  • POST /api/v1/tickets — open a ticket (categorysupport|coordination|refund|emergency; optional bookingId/refundId; body optional). Opener is auto-added as the first participant. Returns { ticketId, referenceCode, status, category }.
  • POST /api/v1/tickets/{id}/messages — post a message. isInternal is staff-only; a non-staff caller sending true403; posting to a closed ticket as non-staff → 403.
  • POST /api/v1/tickets/{id}/participants (add) / DELETE …/participants/{userId} (soft-remove) — staff or ticket owner. A duplicate add is 409 (backed by UNIQUE(ticket_id, user_id)), never a 500.
  • POST /api/v1/tickets/{id}/close · /reopen — participant or staff (idempotent).
  • POST /api/v1/tickets/emergency — assigned nurse (or staff) logs an emergency (+ optional support alert).
  • GET /api/v1/tickets — my tickets (paginated, filter status, search referenceCode).
  • GET /api/v1/tickets/{id}user thread view: internal notes are stripped in the projection.
  • GET /api/v1/admin/tickets + GET /api/v1/admin/tickets/{id} — admin queue + admin thread view: internal notes included (support/admin).

The rule f14 must respect: never build a direct nurse↔customer channel, never surface a phone number, and never rely on the UI to hide internal notes — the backend already strips them from the user payload. The coordination ticket for a booking is auto-created on confirmation (you don't create it).

Partner centers + merchant-of-record (f15 admin + partner consoles)

  • POST /api/v1/admin/partner-centers (create, inactive) · PATCH …/{id} (update) · POST …/{id}/verify (activate) · POST …/{id}/sponsor-nurse · GET … (list) · GET …/{id} (detail) — admin/super_admin. settlementIbanMasked (last 4) is the only IBAN ever returned — never plaintext. commissionRate ∈ [0,1); a merchant-of-record center requires a settlementIban.
  • GET /api/v1/centers/{id}/dashboard — the center's own account (or staff): sponsored nurses + booking/invoice counts + masked settlement summary.
  • GET /api/v1/internal/bookings/{bookingId}/center — the issuer/settlement resolver (platform | partner_center).

Admin backoffice (surfaced, not rebuilt)

  • Support-alert worklist: GET support_alerts/get_support_alerts, POST …/assign_support_alert, POST …/resolve_support_alert (built b1). Audit viewer: GET audit/get_audit_trail (built b1). Both DynamicPermission. Verification queue / refunds / payout dashboard / moderation queue are their own phases' routes — surface them under the admin console with the right RBAC scope.

RBAC the frontend must respect (per route)

  • Authenticated (own) ticket routes: any logged-in user; participation is enforced server-side.
  • Admin ticket queue + admin thread: support/admin. Partner centers: admin/super_admin. Center dashboard: the center's adminUserId (or staff). Support alerts: support/admin. Audit: super_admin/admin.
  • The admin role passes every DynamicPermission check; narrower staff scopes (support/finance/moderation) are granted via seeded role claims.

What's mocked

  • ILicenseVerificationService (eNamad / MoH establishment-permit) — MockLicenseVerificationService, manual-approve at MVP (NeedsManualReview); VerifyPartnerCenter records the human decision. Config Seams:LicenseVerification:AutoApprove forces Valid. See the mock registry (🟡). There is no telephony seam — the emergency call is an out-of-platform tel: link by design.

Contracts

  • dev/contracts/domains/messaging-notifications-admin.md (this phase). swagger.v1.json refreshed (now includes /tickets, /admin/tickets, /admin/partner-centers, /centers, /internal/bookings/{id}/center).

Types / wire notes

  • Envelope unchanged (camelCase body, snake_case URL tokens where [action]-based; the new controllers use explicit REST routes). Pagination page/pageSize (default 50, max 100). sentAt/closedAt/verifiedAt are UTC ISO-8601; ids are numbers; the settlement IBAN is a masked string ("••••0001").