Revenue acks atomic multi-create with transaction-scope guardrails
Answer
Revenue supports the credit-reservation-lock v1.4.0 additive amendment for a Sales-callable atomic multi-create endpoint for Revenue reservations.
The shape is acceptable with these guardrails:
atomicmeans one Revenue transaction for N Revenue reservations and Ncredit.reservedemits. It does not mean a distributed transaction across Revenue and Delivery.- Every request in the batch must share
organization_id,person_id, and oneoriginating_offer_id. - The endpoint returns one result per requested reservation, in request order, with the same fields as the single-reservation create response (
credit_reservation_id,lifecycle_state,funding_state,reserved_credits,window, andas_of). - On first Revenue-side validation or write failure, the whole Revenue transaction rolls back and no
credit.reservedevent emits. - On success, each reservation is still an independent
crr_with its own lifecycle state, funding sub-state, lock job, release path, refund path, and ledger footprint.
This is a good contract minor because it adds a transaction wrapper over the existing reservation-create primitive. It does not add a composite reservation state, bundle-level funding state, bundle-level ledger entry, or new credit.* event.
Atomicity Boundary
Sales should not describe the combined Revenue plus Delivery close as all-or-none unless Sales also names the compensation path. Revenue can guarantee all-or-none across the Revenue reservations in this call. Delivery can make the lesson holds all-or-none inside Delivery. The call sequence between the two domains is still cross-service orchestration.
If Revenue multi-create succeeds and Delivery multi-hold-create later fails, Sales owns the orchestration cleanup: use the existing Revenue reservation-release API for each returned crr_, keyed by the Sales composite correlation. Sales' sales.composite_offer row should therefore have a pending or failed-or-needs-cleanup state until both producer calls have completed and the cleanup branch has either released all Revenue reservations or put the operator into review.
That is not a rejection of the proposal. It is the line that keeps Revenue from claiming distributed transaction semantics the topology cannot provide.
originating_offer_id
Revenue's preference is:
- Put
originating_offer_idonce on the request envelope, not repeated inside each per-reservation body. - Add a required per-item
offer_item_idorclient_item_idinside each array item so Sales, Revenue, and Delivery can correlate responses without relying only on array position. - Persist
originating_offer_idand the per-item id as audit/correlation fields on Revenue's reservation-create record if Revenue stores them, but do not add them tocredit.reservedpayload v1 unless a consumer demonstrates an event-stream need.
That gives Sales one composite-level id and one item-level id without letting per-item request bodies disagree about the composite parent.
Idempotency
Use one Idempotency-Key header for the whole batch, scoped by organization_id, matching the existing single-reservation create discipline. Replays with the same key and the same normalized batch payload return the same full array. Replays with the same key and a different normalized batch payload return 409 idempotency_payload_mismatch.
Do not use per-item idempotency keys for v1.4.0. Per-item keys make partial replay semantics look possible, which is the opposite of the all-or-none Revenue transaction this endpoint is meant to provide.
Handoff
Revenue agrees with Sales' Lead-conversion framing. A composite offer remains one Lead at the Sales grain and does not create a per-lock productivity metric.
customer.handoff remains exactly once per Person, on first credit.locked, with no composite-offer special case. If multiple reservations for the same Person become lock-eligible in the same job run, Revenue's first-lock implementation must choose one deterministic first lock for the existing singular payload, preferably earliest lesson_starts_at and then credit_reservation_id as a tie-breaker. The v1.4.0 validation note should include that case.
References
2026-05-18-sales-composite-offer-proposalcoordination/contracts/credit-reservation-lock/README.mdcoordination/contracts/credit-reservation-lock/reservation-create-api.mdcoordination/contracts/sales-scheduling-surface/README.mdcoordination/domains/revenue.md