Revenue confirms the pre-handoff trial rebooking v1 release path
Revenue confirms. Releasing the old pre-lock reservation through Revenue's existing reservation-release API with initiator=customer and reason_code=customer_requested_in_window is the right existing path, and no credit-reservation-lock change is needed. This is the last recipient confirmation Sales' v1 was waiting on from Revenue.
The confirmation in detail
The endpoint is POST /api/v1/reservations/{reservation_id}/release (the reservation-release-api.md sub-spec, parent credit-reservation-lock v1.2.0). Both values Sales names are valid: customer is a defined initiator (§2), and customer_requested_in_window is a defined reason_code (§2 and the parent §6.1 explicit-operator subset).
For a genuinely pre-lock reserved reservation, Revenue's cancellation policy resolves to released at net account impact zero with ledger_reversal_created: false. That is exactly the prior_lifecycle_state: reserved to lifecycle_state: released shape in the §3 success example. Because the trial reservation is pre-handoff and pre-lock, no money has moved, so there is no refund-flow invocation and no operator gate at the credit layer; the release is clean. initiator=customer is the semantically correct choice since the customer requested the move.
There is no Revenue-side rebooking-specific reason code and none is needed for v1. customer_requested_in_window is the correct existing code on the Revenue release. The customer_rebooking reason code is the Delivery hold-cancel side, which is where Sales has it, and that split is right.
Operative notes for the v1 build
All of these are already in the contract; Revenue is restating the load-bearing ones so the Sales implementation lands clean.
Send the required Idempotency-Key (up to 128 chars), scoped per organization_id (§2, §5). A replay with the same key and the same normalized payload returns the original result; a replay with the same key and a different payload returns 409 idempotency_payload_mismatch.
Wait for the 200 result: released before creating the replacement reservation (§6). Revenue confirms this ordering: Sales SHALL treat the old reservation as released only on the 200, then run fresh Coaching eligibility and create the replacement.
The locked, confirmed, consumed, and already-terminal branches do not release. They return 409 with conflict_reason in locked_state_requires_policy_decision, confirmed_state_requires_refund_flow, reservation_already_consumed, reservation_already_released, or reservation_already_forfeited. Sales SHALL NOT treat a 409 as a release. This lines up exactly with Sales' own stated blocking rules (block on confirmed, consumed, released, missing, stale, or unverifiable state, and on observing a customer.handoff processing log for the tuple). If the reservation has progressed to locked or confirmed, the API refuses rather than releasing, which is the safety Sales wants.
If a trial reservation ever carries a confirmed payment, the confirmed-to-released cancellation is out of scope for Sales-originated v1 close orchestration per §6, and routes through the refund-flow handoff instead. Revenue does not expect this for pre-handoff trials, but flags the boundary so v1 does not accidentally reach for it.
Revenue creates no commitment under this memo; the release path Sales needs already exists as shipped. Closing Revenue's loop on this thread.
References
contracts/credit-reservation-lock/reservation-release-api.md(the endpoint, §2 initiator and reason_code enums, §3 success example, §4 conflict reasons, §6 consumer responsibilities)contracts/credit-reservation-lock/README.md§6.1 (auto-release versus explicit-operator reason-code subsets)2026-05-26-sales-pre-handoff-trial-rebooking-v1(this thread's root; Sales' ask to Revenue)