Skip to content

The contract repository owns public API schemas, generated clients, and handoff checks.

  • Contract tests are colocated and use *_test suffix.
  • Generated clients are consumed through approved wrappers.
  • Backend and frontend do not invent parallel API shapes.
  • Source status codes must be named and mapped in contract types.
  • Document contracts use lower_snake_case definitionKey, templateKey, repeat section keys, and idempotent renderRequestCode.
  • Promotion entitlement contracts expose lower_snake_case entitlement codes; source coupon product codes map only at the boundary.
  • Customer engagement contracts expose Strapi only as the CMS provider and collection names. App-facing vocabulary is contentPublication, campaignPlacement, customerInquiry, memberNotification, and motherLeadRequest.
  • Communication delivery contracts expose Novu only as the workflow provider. App-facing vocabulary is communicationWorkflowRule, communicationPreference, communicationDispatchRequest, and source notification names map only at the boundary.
  • Reservation birth-information contracts expose ReservationBirthProfile, ReservationBirthChildRecord, and BirthInformationReminderTask. Child records are arrays with contiguous sequence values, not fixed child slots. The reminder workflow code is reservation:birth_information_requested.
  • Reservation booking workflow contracts expose ReservationBookingWorkflowStartInput, ReservationBookingWorkflowStartResponse, workflow engine restate, and lower-case workflow statuses. POST /reservation-booking-workflow-starts is implemented by the backend FastAPI boundary and returns 202 Accepted when Restate accepts the command. The stable key uses reservationBookingRequestId first, then motherProfile.memberId plus scheduleStart as the fallback. The input payload is a workflow context snapshot: motherProfile, branchContext, branchActor, caregiverCandidates, caregiverProfiles, and careEnvironment. Matching candidate snapshots include acceptsPetHousehold and petAllergySpeciesCodes so requested-caregiver and smart-matching paths use the same policy facts. Python Pydantic contracts keep BaseModel inheritance at SanmopiaContractModel only; booking payloads derive through semantic intermediate models such as ReservationBookingMotherIdentitySnapshot, ReservationBookingBranchSnapshot, ReservationBookingActorSnapshot, ReservationBookingCareEnvironmentSnapshot, ReservationBookingCaregiverMatchingSnapshot, ReservationBookingCaregiverProfessionalSnapshot, MotherBookingCatalogOption, and ReservationBookingWorkflowEnvelope. It must not reintroduce flat source-era fields such as memberId, branchId, actorRole, reservationStart, or candidateCaregiverIds at the top level. Frontend and backend must not create parallel workflow response shapes. Portal workflow progress reads use reservationBookingWorkflowStatusProjectionPath() and the shared auth-scoped WorkflowStatusProjection; the older /reservation-booking-workflow-requests/{reservationBookingRequestId} row reader requires the same Supabase bearer auth when exposed by the composed backend app, and must not drive actor command rendering. branchActor.actorUserId is the single Supabase Auth-aligned authenticated user identity for request submission authorization, workflow projections, and domain actor models. branchActor.role carries the branch organization domain role label such as 지점 운영자; branch_operator remains only the technical actor kind or SpiceDB relation name. Raw workflow keys stay internal to Restate/Supabase ledger rows. Starting the workflow requires the submit_booking_request capability at draft stage and branch manage permission in SpiceDB. Permission failure returns 403; missing workflow runtime wiring returns 503 instead of silently falling back to an in-process mock.
  • Mother booking frontend requires a backend-provided booking operational context before it can build that workflow input. The context contains the authenticated mother profile, branch context, branch actor, caregiver candidates/profiles, care environment, service options with pricing identifiers and frozen amount hints, and payment method options. Frontend production code must fail closed when this context is absent; test fixtures are the only place allowed to hold sample branch/caregiver/price/environment values. GET /mother-booking-operational-context requires a Supabase bearer token and reads the versioned Supabase read model for that mother. Missing bearer returns 401, no current context returns 404, and missing backend wiring returns 503. The response is a frozen booking UX context, not a place to recalculate price or caregiver policy after the fact.
  • Service-calendar compatibility contracts expose POST /service-calendar-compatibility-decisions for historical reservation calendar decisions. The request sends a frozen plannedServiceCalendar snapshot, optional branchProfileId, optional sourceServiceEndOn, and optional sourceReference; backend derives the actor from Supabase Auth and checks branch view permission when a branch profile is scoped. The response uses named decisions use_current_policy, preserve_source_service_end, or require_operator_review with policyFingerprint, plannedServiceEndOn, optional sourceServiceEndOn, reasonCode, and optional source reference. Clients must use serviceCalendarCompatibilityDecisionsPath() and serviceCalendarCompatibilityDecisionRequestHeaders() and must not reimplement legacy 1-week/2-week/weekend/holiday drift logic in UI code. Stored impact projections are read through GET /reservation-service-calendar-impacts/{reservationBookingId} with optional planCode and impactKind filters. The response includes the persisted compatibilityDecision beside impact facts, so pricing, report, caregiver scheduling, settlement, and refund consumers read the same frozen policy evidence instead of recalculating duration drift locally. Clients must use reservationServiceCalendarImpactsPath() and reservationServiceCalendarImpactRequestHeaders().
  • Reservation financial lifecycle contracts expose GET /reservation-financial-lifecycles/{reservationBookingId}/mother-visible-charge-summary for finalized mother-facing charge display. The response uses the frozen MotherVisibleChargeSummary snapshot and never recalculates from later caregiver payout, branch settlement, or HQ settlement changes. Unfinalized charge summaries return 404; missing read-model wiring returns 503.
  • Operator reservation detail contracts expose POST /operator-financial-mutation-lock-decisions/{reservationId} for branch/HQ financial edit previews. The request carries branchActor, reservationStatus, serviceEndOn, evaluatedAt, and optional requestedFields; it does not accept actorKind. Backend derives actorKind from the authenticated branch/HQ actor context, checks the Supabase bearer token against branchActor.actorUserId, and returns field-level lock payloads with policyVersionLabel, reasonCode, messageKey, requiredCapabilities, and sourceAnchor. Portal clients must render these lock facts and must not recreate source cantSave or date/status branching in UI code. Mutation commands still need expected-revision re-checks before writing reservation or payment facts.
  • Branch operations contracts expose BranchSettlementAccount, SnapshotBranchSettlementAccountForStatementCommand, and BranchSettlementAccountSnapshot shapes. Account purpose is business-specific (statement_remittance, branch_deposit, membership_fee, refund), raw account numbers are forbidden, and statement snapshot commands freeze account revision, actor, and idempotency key before settlement documents read the account. Backend persistence now stores current purpose-scoped branch accounts and profile revisions through Supabase service-role tables. POST /branch-settlement-account-statement-snapshots persists immutable statement-account evidence, derives the actor from Supabase Auth, and rejects stale account revisions. The contract repo exposes the same API through OpenAPI, JSON Schema, and TS helpers branchSettlementAccountStatementSnapshotsPath() and branchSettlementAccountStatementSnapshotRequestHeaders(). POST /branch-office-profile-updates revises editable profile facts with Supabase Auth-derived actor id, branch manage permission, expected revision, command id, revision id, reason, and server effective timestamp. Stale saves return 409 and clients must build the command endpoint through branchOfficeProfileUpdatesPath() and branchOfficeProfileUpdateRequestHeaders(). POST /branch-office-lifecycle-changes moves branch office lifecycle through onboarding, active, suspended, and closed using the same actor, branch manage permission, expected revision, command id, revision id, reason, and server effective timestamp. Clients must use branchOfficeLifecycleChangesPath() and branchOfficeLifecycleChangeRequestHeaders(), not source delete flags or inline status constants. POST /branch-settlement-account-replacements replaces one purpose-scoped settlement account with masked-only account facts, expected revision, command id, revision id, reason, and server recorded timestamp. Clients must use branchSettlementAccountReplacementsPath() and branchSettlementAccountReplacementRequestHeaders(); raw account numbers are not part of the public contract. POST /branch-service-area-policy-revisions publishes the full revisioned branch coverage policy from backend-owned rule drafts. Rules carry semantic policyKind values (available, consultation_only, unavailable), address depth from populated sido/sigungu/bname, effective windows, and the Supabase Auth actor captured through branch manage permission. POST /branch-service-area-policy-decisions resolves one service date and address into a frozen coverage decision with Daum/Kakao/operator evidence, matched rule id, revision number, address depth, and decision code. Clients must use branchServiceAreaPolicyRevisionsPath(), branchServiceAreaPolicyRevisionRequestHeaders(), branchServiceAreaPolicyDecisionsPath(), and branchServiceAreaPolicyDecisionRequestHeaders() instead of UI-side AREA_ACCEPT_TYPE_ID_PK or address-depth branching. Remaining branch profile mutation clients for membership fee, credentials, and assets stay explicit follow-up work.
  • Family-account authority contracts expose POST /family-reservation-payment-authorizations, and POST /family-reservation-care-history-access-decisions. The care-history decision request contains only familyAccountId, reservationBookingId, and scope; it accepts no mother or grantee identity. Backend derives the acting member from the Supabase profile and the account mother from persisted family authority, rejects withdrawn sessions, and returns the active ReservationCareHistoryAccessGrant snapshot. Clients must use familyReservationPaymentAuthorizationsPath(), familyReservationCareHistoryAccessDecisionsPath(), and familyAccountAuthorityRequestHeaders(). UI contact fields such as emergency contact, relationship text, or payment-notice phone numbers are contact evidence only, not authority facts.
  • Reservation care-history lifecycle contracts expose POST /family-accounts/{familyAccountId}/reservation-care-history-access-grants and POST /family-accounts/{familyAccountId}/reservation-care-history-access-grants/{reservationCareHistoryAccessGrantId}/revocations. Grant accepts grantee, booking, scopes, expiration, and idempotency; revoke accepts expected revision, reason, and idempotency. Both derive the mother actor from authentication. Clients must use reservationCareHistoryAccessGrantsPath() and reservationCareHistoryAccessGrantRevocationsPath(). A read grant never authorizes daily-report review or caregiver rating mutation.
  • Caregiver compensation contracts expose CaregiverPayoutAcknowledgementCommand and CaregiverPayoutAcknowledgementReadModel so branch deposit state and caregiver receipt state are backend-owned facts with actor, expected revision, idempotency key, and replay guard. Public API routes now include POST /caregiver-payout-acknowledgements/branch-deposits, GET /caregiver-payout-receipt-states/{reservationBookingId}/{caregiverPayoutInstructionId}, POST /caregiver-payout-receipt-states/acknowledgements, POST /caregiver-payout-receipt-states/exceptions, and POST /caregiver-payout-receipt-states/repairs. Frontend must not finish payout or reservation completion from editable UI amount fields.
  • Reservation payment workflow contracts expose POST /reservation-payment-pre-registrations for Kill Bill REST amount pre-registration before payment UI entry. The request carries reservationBookingId, paymentId, paymentMethod, paymentPurpose, and amountKrw; the response returns preRegistrationStatus, optional gateway code/message, and the frozen payment facts. A reused paymentId is accepted only when the waiting local payment record matches the same reservation, method, purpose, and amount. Reservation payment records persist payment_purpose from the ubiquitous purpose enum and set deposited_at when Kill Bill verification or a direct booking write moves the local record into a paid status.
  • Payment product-plan catalog contracts expose GET /payment-product-plans and GET /payment-product-plans/{planCode} behind Supabase bearer auth. The catalog is derived from active/effective Supabase sanmopia_payment_product_plan_catalog_items rows and returns stable plan codes, product family, allowed payment methods, ledger obligation purposes, provider routing key, settlement participation, realization timing, fee basis, refund policy, conversion policy, and virtual-account expiration policy. Frontend payment flows must select and display these backend facts; they must not derive product meaning from source payment type ids, payment purpose alone, or hardcoded UI amounts.
  • Operator manual reservation payment receipt contracts expose POST /reservation-payment-receipts for protected service-balance and customer-share-balance receipt recording. The request carries reservationBookingId, paymentId, paymentMethod, paymentPurpose, amountKrw, depositedAt, expectedPaymentRevision, and idempotencyKey; the response returns those same frozen payment facts plus paymentFactRevision and recordedAt. This command creates a new manual receipt fact at revision 0 only. Existing payment fact changes must use the operator payment-fact edit command so stale screens cannot overwrite paid, refunded, or settlement-bound rows.
  • Reservation payment workflow contracts expose POST /reservation-payment-workflow-starts and GET /reservation-payment-workflow-requests/{paymentWorkflowRequestId}. Progress responses include gateway result, approved charge finalization status, finalized charge snapshot id, and settlement continuation status. settlementContinuationStatus = waiting_for_financial_facts means payment closeout is done but the next financial lifecycle workflow is waiting for service-delivery, payout, branch, or HQ ledger facts; clients must poll the workflow status projection instead of calling Restate directly.
  • Reservation service-offering availability contracts expose POST /reservation-service-offering-availability-decisions behind Supabase bearer auth. The request carries candidate offering, evaluation context, optional shutdown windows, and policy revision; the response returns backend availability state, handoff kind, unavailable reason codes, message keys, and source evidence keys. Real reservation screens should call POST /reservation-service-offering-availability-decision-sets, which reads effective service-offering candidates and shutdown windows from Supabase care service catalog offerings and policy attachments before returning the decision set. Astro reservation cards may render these decisions, but must not decide pre-reservation windows, voucher direct-service blocking, in-home exclusion, continuation routing, or year-boundary shutdown copy from PHP/JS constants.
  • Reservation cancellation refund contracts expose POST /reservation-cancellation-refunds. The request carries paymentChangeAdjustmentId only as a durable link to a paid change-payment adjustment; the backend still derives refundable balance from the payment ledger, quotes refund deductions from the active adjustment catalog, and returns frozen refund lines plus the linked adjustment id. Clients must not send UI-calculated paid/refund totals.
  • Internal provider refund repair contracts expose POST /internal/payment-refund-repairs/provider-refund-plans for provider-success/local-write-failure repair planning and POST /internal/payment-refund-repairs/provider-refund-executions for audited Kill Bill refund execution attempts. Both routes are SANMOPIA_INTERNAL_JOB_TOKEN gated. Planning accepts backend-owned PaymentLedger, PaymentRefundIntent, adjustment catalog, approved repair case, provider lookup, and SpiceDB-derived ProviderRefundRepairPermission, then persists PaymentRefundRepairPlan. Execution accepts the persisted plan identity, refund amount, settlement invalidation key, idempotency key, and evidence keys. The handler first looks up an existing execution attempt by idempotency key; a matching attempt is replayed into the ledger-backed provider-result recorder without calling the billing gateway again, while a mismatched replay is rejected. New attempts call the configured billing gateway and persist the execution result before recording accepted or rejected repair results. Refund amount and paid-row revision still come from backend ledgers. Public clients must not call these routes or send raw provider ordr_idxx/tno refund commands. External webhook E2E proof, retry backoff and operator result-history projection, and downstream reporting/artifact invalidation proof still belong to backend workers and provider-result handlers.
  • Payment-change adjustment contracts expose POST /reservation-payment-change-adjustments, POST /reservation-payment-change-adjustments/{reservationBookingId}/mother-payment-pre-registrations, POST /reservation-payment-change-adjustments/{reservationBookingId}/mother-payment-verifications, POST /reservation-payment-change-adjustments/{reservationBookingId}/mother-payments, POST /reservation-payment-change-adjustments/{reservationBookingId}/zero-payable-closeouts, GET /reservation-payment-change-operator-board, GET /reservation-payment-change-adjustments/{paymentChangeAdjustmentId}/settlement-continuation-workflow-status, POST /reservation-payment-change-adjustments/{paymentChangeAdjustmentId}/operator-completions, and POST /reservation-payment-change-adjustments/{paymentChangeAdjustmentId}/operator-amount-corrections. These endpoints require Authorization: Bearer <Supabase access token>. Operators need booking-scoped SpiceDB permission to request, complete, or audit-correct an adjustment; mother payment/pre-registration/verification and zero-payable closeout require the authenticated Supabase user id to match motherUserId. Zero-payable closeout uses PaymentChangeAdjustmentZeroPayableCloseoutPayload with a client idempotency key, but amount/payment receipt authority stays backend-owned as zero_payable_closeout. Completion and amount-correction commands write append-only PaymentChangeOperatorAudit facts instead of rewriting paid totals. Amount-correction requests must carry expectedAdjustmentUpdatedAt copied from the operator-board row; stale submissions return conflict instead of appending audit (f9ce5dc, 05059a5). Operator-board rows now enrich paid/completed adjustments with reservationId, branchProfileId, motherDisplayName, and reservationChargeAmountKrw from Supabase reservation/member profile read tables while preserving technical ids for authorization and durable links. Rows also expose nullable payment milestone facts customerShareDepositPaidAt, customerShareBalancePaidAt, serviceBalancePaidAt, and preReservationDepositPaidAt from sanmopia_payment_records.payment_purpose plus deposited_at when those facts exist. Each operator-board row also exposes manualPaymentReceiptCommandDraft with backend-owned command availability, disabled reason, command draft id, booking id, payment id, canonical payment method, payment purpose, amount, deposited-at time, and idempotency key so clients do not infer receipt defaults from labels, row status, or UI-side amount/date arithmetic. Backend proof: 4bd81e1, 455f74a; TypeScript contract proof: 858fa24, 87a6e61. Adjustment and operator-board responses include nullable settlementContinuationStatus, settlementContinuationWorkflowRequestId, settlementContinuationReason, and settlementContinuationRecordedAt fields after paid adjustment settlement continuation is requested. The adjustment-id workflow-status endpoint maps a paid adjustment to its shared reservation_financial_lifecycle projection, so operator screens can poll progress without calling Restate or guessing a workflow key. Clients must build paths and the bearer header with the exported TypeScript helpers, including paymentChangeAdjustmentRequestHeaders(), and must not read sanmopia_payment_change_adjustments or sanmopia_payment_change_operator_audits directly from the Supabase Data API.
  • Reservation financial lifecycle workflow contracts expose POST /reservation-financial-lifecycle-workflow-starts for Restate-backed downstream finance closeout. The request carries financialLifecycleWorkflowRequestId, reservationBookingId, optional service-delivery facts, optional caregiver/branch/HQ ledger-line groups, and completeSettlement. The response returns workflow name ReservationFinancialLifecycleWorkflow, workflow engine restate, stable workflow key, invocation id, and accepted status.
  • Caregiver compensation contracts represent each caregiver through a contractAssignmentKey, separate from handoff order. Quote lines must carry rateRuleId, policyVersionLabel, rateEffectiveFrom, and optional rateEffectiveUntil so frontend, backend, and Supabase read the frozen payout basis instead of recalculating against later grade-rate policies. Quote inputs use rateEffectiveOn when more than one grade/role rate line exists; the backend rejects ambiguous versioned rates instead of guessing current policy.
  • Daily care report contracts separate caregiver-authored DailyCareReportSubmission from customer-authored review facts. CustomerDailyReportReview carries the reviewed source report code, optional CaregiverServiceRating, and optional CustomerDailyReportReviewRewardGrant, so final-day rating and reward-point issuance do not hide inside printable document rendering. Authenticated customer routes expose readiness and submission, and the backend records a DailyReportCompletionRewardLedgerInstruction for reward fulfillment instead of leaving coupon/point side effects to frontend state. Reward lines now move through a Supabase PGMQ worker contract with dispatch status on each line; the worker records idempotent service-role-only fulfillment facts, while the authenticated customer projection GET /customer-daily-report-reviews/{reservation_booking_id}/reward-status exposes applied and missing reward components from those facts. The external shopping-mall/coupon/point connector remains a separate contract.
  • Reservation document delivery contracts separate rendered document artifacts from recipient access and notification attempts. ReservationDocumentDelivery owns DocumentAccessGrant, DocumentRecipientNotification, and DocumentDeliveryAttempt facts with explicit grant state, trigger, channel, retry timestamp, and message template. Clients must not infer access from a render artifact alone.
  • Reservation change-request contracts expose ReservationChangeRequest, ReservationChangeApplicationPlan, ReservationChangeEffect, and ReservationChangeOperatorAudit. Change intake describes requested facts; accepted application plans describe effects on schedule, payment, caregiver assignment, compensation, mother-visible summary, and branch settlement. Clients must not directly mutate assignment or settlement rows from a request payload.
  • Reservation authorization contracts expose ReservationCapabilityAuthorizationSnapshot. Backend snapshots combine reservation-stage capability policy with SpiceDB relationship checks and return allowed/denied capabilities with stage and relationship decision reasons. Frontend may use reservationStageCapabilityDecision only as a stage-only pre-render helper; relationship authorization remains backend-owned.
  • Reservation operation command contracts expose backend-decided command boards and applied operation plans. Accepted command responses may include followUpWorkflowRequestId when the plan starts asynchronous follow-up work. Clients must display the operation plan and poll WorkflowStatusProjection kind reservation_operation_follow_up; they must not call Restate or service-role Supabase workflow tables directly.
  • Reservation collaboration contracts expose POST /reservation-collaboration-workspaces/{reservationId}, POST /reservation-collaboration-workspace-events/{reservationId}, POST /reservation-collaboration-journal/{reservationId}, and POST /reservation-collaboration-commits/{reservationId}. Workspace open returns currentRevision, realtimeTopic, currentValuesByField, latestJournalEntry, coordinationPolicy, and draftSession. The draftSession includes baseRevision, latestKnownRevision, status, openedAt, lastSeenAt, and dirtySince so clients can separate local draft state from newer server revisions. Workspace-event requests record navigation_blocked, draft_marked_dirty, draft_recovered, draft_discarded, commit_conflicted, and workspace_closed without pretending those UX events are reservation fact mutations. Commit requests require branchContext, branchActor, actorKind, actorProfileId, expectedRevision, idempotencyKey, sourceWorkspaceId, mutationKinds, and lower_snake dot-separated changeSet field paths. branchActor.role is the organization role label such as 지점 운영자; branch_operator remains the technical actorKind. Accepted commits return 201 with committed revision, audit event id, realtime projection event, and navigation intent. Stale saves return 409 with currentRevision, conflictingFields, and refreshRequired; this includes races where the revision changes between the workspace read and the Supabase RPC commit. Frontend clients must use the exported TypeScript helpers for paths, bearer headers, idempotency keys, realtime event name, and validation; handwritten collaboration fetch shapes are not allowed.
  • Workflow status projection contracts expose GET /workflow-status-projections/{workflowKind}/{workflowRequestId} for portal polling across reservation booking, reservation operation follow-up, payment, financial lifecycle, member privacy, and branch settlement reconciliation export workflows. The endpoint requires a Supabase bearer token and returns 401 for missing tokens, 403 for denied SpiceDB or ownership checks, 404 for unknown projections, and 503 for missing backend wiring. Reservation booking projections include reservationStatus and, when the authenticated actor can be mapped to mother, caregiver, branch_operator, or hq_admin, a reservationCapabilityAuthorization snapshot. Frontend clients must prefer that backend snapshot over local stage-only helpers for rendered actions. Clients build the header through workflowStatusProjectionRequestHeaders and must not call Restate APIs directly.
  • Branch settlement reconciliation exports expose backend-owned settlementDataRoomReadiness inside GET /branch-settlement-reconciliation-exports/{branchProfileId}. The projection carries collectionCode, collection, readinessStatus, nullable settlementPeriodStart and settlementPeriodEnd, branch/external recipient scope, source/projection/expected revisions, rowCount, sourceEvidenceKeys, blockingFacts, and updatedAt. Empty exports must report null period coverage instead of fake dates. Frontend clients must use that projection and the shared contract validator; contract_gap is allowed only during staged rollout when the field is absent.
  • Settlement data-room contracts expose POST /settlement-data-room-export-requests, GET /settlement-data-room-requests/{settlementDataRoomRequestId}, POST /settlement-data-room-requests/{settlementDataRoomRequestId}/repair-requests, and POST /settlement-data-room-requests/{settlementDataRoomRequestId}/download-handoffs. Internal workflow contracts expose POST /settlement-data-room-requests/{settlementDataRoomRequestId}/builds and POST /settlement-data-room-requests/{settlementDataRoomRequestId}/ready-artifacts. SettlementDataRoomCollection separates external_counterparty_settlement, branch_to_headquarters_settlement, headquarters_settlement, and inter_office_settlement; external-counterparty requests require externalRecipientCode. Request payloads carry scope, sourceDatasetRevision, sourceEvidenceKeys, revision, immutable artifacts, and append-only actionJournal. Ready requests require an active artifact; repair creates a new request action and supersedes old artifacts instead of mutating or deleting files. Download handoff responses return a short-lived signed URL and committed revision without exposing storage bucket or object key. Clients must use settlementDataRoomRequestPath, settlementDataRoomBuildsPath, settlementDataRoomReadyArtifactsPath, settlementDataRoomRepairRequestsPath, settlementDataRoomDownloadHandoffsPath, settlementDataRoomExportRequestsPath, and settlementDataRoomRequestHeaders; hidden iframe download flows are not part of the modern contract. Operator workspace contracts additionally expose GET /settlement-data-room-workspaces/{workspaceCode}?collection=..., POST /settlement-data-room-workspaces/{workspaceCode}/artifact-requests, POST /settlement-data-room-workspaces/{workspaceCode}/repair-requests, POST /settlement-data-room-workspaces/{workspaceCode}/grant-handoffs, and POST /settlement-data-room-workspaces/{workspaceCode}/return-state. These endpoints return SettlementWorkspacePayload plus the active BulkExportTemplatePolicyPayload, require Supabase bearer auth, derive the settlement data-room actor profile from backend-owned member profiles, and authorize branch/HQ/workflow scope through SpiceDB. Workspace grant revocation is backend-owned: artifact grants expose revokedAt, revokedByRepairRequestCode, and revokedReason; repair requests expose revokedGrantIds and replacementArtifactRequestCode so clients can show stale download refusal and reissue lineage without recalculating it in UI. Workspace collections are external_counterparty_settlement, branch_to_headquarters_settlement, headquarters_settlement, and inter_office_settlement; clients must not render or mutate source dataType, board id, or numeric status ids. Runtime persistence lives in service-role-owned Supabase tables sanmopia_settlement_data_room_workspaces and sanmopia_settlement_data_room_template_policies with explicit grants, RLS, JSONB aggregate snapshots, and queryable scope/revision columns.
  • Settlement statement contracts expose GET /settlement-statements/{settlementStatementId}, POST /settlement-deposit-requests, POST /settlement-branch-deposit-acknowledgements, POST /settlement-headquarters-settlement-approvals, and POST /settlement-arrears-deposit-requests. The statement contract separates messageToBranch from headOfficePrivateMemo, freezes SettlementRemittanceAccountSnapshot, and records SettlementTransitionAudit with before/after status, expected revision, committed revision, actor, and idempotency key. Branch manager acknowledgement includes SettlementStatementLineAcknowledgement[] so each settlement line can be accepted or disputed without hiding missing fees in one boolean; each acknowledgement can carry sourceEvidenceKey so legacy form evidence remains traceable without leaking source-era booleans into domain commands. Headquarters administrator approval is a separate ApproveHeadquartersSettlement command that moves the statement to settled or arrears. Branch clients must treat headOfficePrivateMemo as forbidden data; generated-client validators flag branch-visible statements that include it.
  • Reservation external-counterparty settlement status contracts expose GET /reservation-external-counterparty-settlement-statuses/{reservationId} with branchProfileId query scope. The response is ReservationExternalCounterpartySettlementStatus, a reservation-scoped projection that replaces source DANBEE_STATUS_FL with completionState, settlementStatementId, settlementStatus, datasetRevision, lineageEvidenceKeys, and updatedAt. The route requires a Supabase bearer token and branch settle permission through SpiceDB; clients build path, header, and validation through reservationExternalCounterpartySettlementStatusPath, reservationExternalCounterpartySettlementStatusRequestHeaders, and findReservationExternalCounterpartySettlementStatusProblems. Internal settlement workflows update that projection through POST /internal/reservation-external-counterparty-settlement-status-updates using Authorization: Bearer ${SANMOPIA_INTERNAL_JOB_TOKEN}. Payloads include reservation id, branch id, settlement statement id, settlement status, dataset revision, lineage evidence keys, and update timestamp. The backend rejects stale dataset revisions so a finished external-counterparty status is not overwritten by an older statement event. Portal clients must not call the internal updater.
  • Customer action availability contracts expose GET /customer-reservation-action-availabilities/{reservationId} and GET /customer-new-reservation-eligibility. Both require a Supabase bearer token and derive customerId from the authenticated user, not from request body fields. The backend reads sanmopia_customer_reservation_action_source_states, sanmopia_customer_reservation_state_mapping_catalogs, and sanmopia_customer_action_policy_rule_catalogs to project legacy source status into current lifecycle language, action availability, command target, disabled reason, idempotency scope, and new-reservation blocking. Portal clients must render these results and must not branch on raw status numbers.
  • Internal scheduled-job contracts expose POST /internal/member-privacy-purge-workflow-triggers for trusted runtime jobs that hand due privacy purge tasks to Restate. The route requires Authorization: Bearer ${SANMOPIA_INTERNAL_JOB_TOKEN} and is not a portal client API. Supabase Cron calls this route through pg_net; stage setup runs pnpm supabase:cron:configure so backend URL and token stay in env-derived database settings instead of SQL literals.
  • Already-issued Supabase access tokens after member withdrawal are not treated as active Sanmopia sessions. Backend APIs reject current Auth users whose app metadata has sanmopia_privacy_status=withdrawn, and Supabase RLS adds a restrictive private.sanmopia_current_member_session_is_active() guard across Sanmopia Data API tables.
  • Branch settlement reconciliation export workflow repair contracts expose POST /branch-settlement-reconciliation-export-workflow-retries/{workflowRequestId} and POST /branch-settlement-reconciliation-export-workflow-cancellations/{workflowRequestId}. Retry is allowed only after failed or cancelled, reuses the frozen snapshot/workflow key, and returns 202; cancellation rejects completed workflows and returns the cancelled repair result. Both endpoints require Supabase bearer auth and branch repair visibility through the backend.
  • Branch settlement operator board contracts expose GET /branch-settlement-operator-boards/{branchSettlementId} for branch/HQ settlement review screens. The endpoint requires a Supabase bearer token, reads Supabase settlement rows through the backend service-role adapter, and checks branch settle permission through SpiceDB before returning data. The response is BranchSettlementOperatorBoard: settlement period, stored net balance, line-derived receivable/payable totals, calculated net balance, reconciliation delta, and ordered directional line items with policyVersionLabel. Clients build the path and header through contract helpers and must not read Supabase settlement tables directly.
  • Kill Bill payment webhook contracts expose POST /killbill-payment-events. The request body is the Kill Bill payment event callbacks JSON envelope with type, timestamp, and data, while unknown provider-added fields remain allowed. The 202 response returns processingStatus = queued, webhook message id, event type, nullable payment id, workload kind payment_webhook_processing, and queue name sanmopia_payment_webhook_processing. The backend worker then records sanmopia_killbill_payment_events and starts ReservationPaymentWorkflow for verify_paid_payment when the event can be mapped to a payment. Paid webhooks classify virtual_account_deposit when the local payment record method is vcnt; other paid records use instant_payment. Cancelled, partially cancelled, failed, and virtual-account-issued webhooks synchronize payment record status from the queue worker after local payment lookup.
  • Kill Bill payment administrator contracts expose POST /killbill-payment-admin-operations. The request carries an action, reason, idempotency key, expected revision, and action-specific payment/search facts; it never accepts client-supplied actorUserId. The backend derives the actor from Supabase Auth, gates branch managers through SpiceDB finalize_charge, records append-only sanmopia_killbill_admin_operation_audits, and returns the audit id, action, status, provider status/result, fingerprints, and replay source when the same idempotency key is reused.
  • Reservation financial lifecycle commands expose POST /reservation-financial-lifecycles/{reservationBookingId}/service-deliveries, .../caregiver-payout-preparations, .../branch-settlement-preparations, .../hq-settlement-preparations, and .../settlement-completions. The response is ReservationFinancialLifecycle: current stage, frozen mother-visible payable total, caregiver payout total, branch settlement total, HQ settlement total, delivered-day facts, and ledger line count. Commands return 404 for missing lifecycle, 409 for wrong stage or bucket, and 503 when the command handler is not configured.
  • Reservation booking command intake must receive mother profile, branch context, actor context, caregiver candidates, and caregiver profile facts from the caller or repository adapter. API mappers must not synthesize placeholder emails, branch ids, actor user ids, caregiver ids, or matching candidates.
  • Booking service-term side-effect assembly contracts expose GET /booking-service-term-side-effect-assemblies/{assemblyKey} for immutable readback and POST /booking-service-term-side-effect-assemblies/{assemblyKey}/projection-invalidation-repairs for operator requeue of failed report/settlement invalidations. The repair command is idempotent, path-scoped to the assembly, authorized through the booking workflow projection, and returns requested/requeued/skipped invalidation keys so frontend does not infer projection state locally.
  • Daily care report contracts expose DailyCareReportDefinition and DailyCareReportSubmission. Form sections and fields are Strapi-authored lower_snake_case keys, submitted answers are keyed values, and source-era view fields such as incisionSite or otherService do not appear in public contracts.
  • Document reporting contracts expose DailyCareReportPrintPayload for A4 report-series rendering. The payload carries ordered dailyCareReports and flattened dailyCareReportAnswerRows with logical answer row keys, section keys, answer keys, display values, caregiver role, handoff order, and policy version so HTML print templates do not rebuild source Excel coordinate or nested-loop logic.
  • Mother lead intake contracts expose modern program codes. Source codes such as MILK, LOVEBOX, and CLEAN_TB are import-boundary mappings only.
  • Caregiver performance recognition contracts expose seasons, metric rules, scorecards, contribution facts, manual adjustments, branch scorecards, and grade thresholds. Source award fields such as JOIN_GRADE, SCORE, INTRODUCE_GRADE, and GRADE_SCORE map only at import boundaries; total fields are never accepted as contribution metrics.