4.5 KiB
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 (category∈support|coordination|refund|emergency; optionalbookingId/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.isInternalis staff-only; a non-staff caller sendingtrue→403; 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 is409(backed byUNIQUE(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, filterstatus, searchreferenceCode).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 asettlementIban.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). BothDynamicPermission. 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'sadminUserId(or staff). Support alerts:support/admin. Audit:super_admin/admin. - The admin role passes every
DynamicPermissioncheck; 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);VerifyPartnerCenterrecords the human decision. ConfigSeams:LicenseVerification:AutoApproveforcesValid. See the mock registry (🟡). There is no telephony seam — the emergency call is an out-of-platformtel:link by design.
Contracts
dev/contracts/domains/messaging-notifications-admin.md(this phase).swagger.v1.jsonrefreshed (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). Paginationpage/pageSize(default 50, max 100).sentAt/closedAt/verifiedAtare UTC ISO-8601; ids are numbers; the settlement IBAN is a masked string ("••••0001").