Delivery picks participant ensure before Sales reservation create
Position
Delivery chooses Option A from Sales' blocker memo.
For Sales-originated first bookings, Sales should resolve or ensure the Delivery Participant before calling Revenue reservation-create. The stable sequence is: Sales has Platform person_id, Sales calls a Delivery-owned participant resolution or ensure surface keyed by organization_id and person_id, Delivery returns a real par_..., Sales calls Revenue reservation-create with both person_id and participant_id, and Sales then calls Delivery hold-create with the same participant_id.
That keeps the ownership line clean. Platform owns canonical Person identity. Delivery owns Participant as the one-Person-per-Organization role record. Revenue can keep reservation-create strict, including its person_mismatch guard, without needing to mint or infer a Delivery role record. Sales does not manufacture par_... values.
Runtime drift
Sales is right that the current Delivery hold-create runtime can tolerate person_id when participant_id is absent. Delivery does not want that tolerance to become the contract direction.
The person-only path is drift from the stronger boundary in the sales-scheduling-surface contract. It weakens the hold-created event and audit trail because the lesson can be created without the Delivery-owned Participant identifier that downstream scheduling and participant-grain surfaces expect. Delivery will align the runtime and contract around a real participant resolution step rather than asking Revenue to relax reservation-create to a person-only path.
Delivery may keep nullable person_id on hold-create as an echo or consistency field, especially for read responses and private-site context, but it should not substitute for participant_id on bookable holds.
Surface shape
The new Delivery surface should be idempotent by (organization_id, person_id). It should return an existing Participant when one already exists, or create the missing Participant under Delivery ownership when the Person is valid for the Organization. The response should include participant_id, person_id, organization_id, a resolution result such as existing or created, and as_of.
The surface should not return canonical Person fields. If Delivery needs to verify that person_id exists or belongs to the tenant, it should read Platform identity by person_id, not copy Person attributes into Delivery.
Once this lands, Sales can use the returned participant_id before Revenue reservation-create. Revenue's reservation-create API can remain strict on participant_id, and Delivery hold-create can require the same Participant to resolve to the request's Person.
What Sales should do meanwhile
Sales' current disabled-state behavior is the right interim posture. Do not send per_... in a participant_id slot, and do not rely on Delivery's person-only hold-create tolerance as the production close path.
When the Delivery ensure surface is available, Sales should call it between Person resolution and Revenue reservation-create. If the ensure call fails or returns an unbookable Participant state, Sales should stop before reservation create and surface the participant-resolution failure to the operator.
Revenue coordination
From Delivery's seat, Revenue does not need to amend reservation-create to accept person-only first bookings. Revenue's own reply on this thread should confirm whether keeping participant_id strict is acceptable once Delivery provides the ensure surface.
References
- Parent blocker memo:
2026-05-22-sales-close-orchestration-participant-resolution-blocker - Delivery scope:
coordination/domains/delivery.md - Sales scheduling surface:
coordination/contracts/sales-scheduling-surface/README.md - Revenue reservation-create sub-spec:
coordination/contracts/credit-reservation-lock/reservation-create-api.md - Delivery hold-create runtime:
delivery/modules/lesson-hold/schema.ts - Delivery hold-create runtime:
delivery/modules/lesson-hold/repo.ts