Create and manage a Cal.diy booking Use this guide to create one booking through slot discovery, an immutable plan, trusted approval, a single commit attempt, and durable verification. It then shows how to choose a safe follow-up operation without replaying an uncertain provider mutation. This guide uses the eventTypeId branch and no custom duration. Use the capability reference when you need slug, team, recurrence, seating, routing, phone-only attendee, or custom-location input. Prerequisites You need: • an admitted and healthy Cal.diy connector for the intended tenant; • a native AIP endpoint and bearer token already bound to that tenant; • a caller authorized to discover and invoke Cal.diy capabilities; • a trusted approval process for tenant-policy decisions; • a known Cal.diy event-type id; • a durable place to retain action, transaction, plan, approval, idempotency, and provider-operation identifiers. Set client coordinates without putting provider credentials in the shell: export AIPURL='https://aip.example.com' export AIPTOKENFILE='/run/secrets/aip-native-token' export EVENTTYPEID='42' The deployment resolves tenant, account, connector route, and provider credential from authenticated context. Do not add them to booking input. 1. Confirm the admitted contracts Inspect the two capabilities used before any mutation: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ capability list "$AIPURL" \ --capability-id cap:caldiy:slot.list \ --limit 1 aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ capability list "$AIPURL" \ --capability-id cap:caldiy:booking.create \ --limit 1 Record the catalog revision plus each contract and schema digest. Confirm that booking.create is medium risk, approval-required, requires external-account idempotency, and requires plan before commit. Catalog visibility proves admission at one revision. It does not prove that a connector replica or Cal.diy account is currently reachable. 2. Discover a bounded slot Choose a future interval and write slot-query.json: { "start": "2030-01-03", "end": "2030-01-04", "eventTypeId": 42, "timeZone": "Europe/London", "format": "time" } Submit a low-risk read with a stable action id: export SLOTACTIONID='actcalbookingslots001' aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:caldiy:slot.list \ --action-id "$SLOTACTIONID" \ --mode sync \ --input @slot-query.json Choose an exact date-time from the provider result. In the remaining examples, the chosen start is 2030-01-03T10:00:00Z. A slot result is an observation, not a hold. If the workflow needs a temporary provider reservation, use the separate reservation lifecycle and preserve its opaque uid. Booking create does not consume a reservation uid in its published input. 3. Freeze one booking input Write booking-create.json once: { "start": "2030-01-03T10:00:00Z", "eventTypeId": 42, "attendee": { "name": "Avery Patel", "email": "avery@example.com", "timeZone": "Europe/London" } } Keep this file byte-stable for the plan and commit. The runtime binds the capability and canonical input hash, not the filename. Reformatting equivalent JSON is normally canonicalized, but changing any semantic value requires a new plan and a newly reviewed commit. Reserve stable identifiers and keep them together: export PLANACTIONID='actcalbookingplan001' export COMMITACTIONID='actcalbookingcommit001' export TRANSACTIONID='txncalbooking001' export BOOKINGKEY='cal-booking-001' Never reuse BOOKINGKEY with different canonical input or another external account. 4. Create the downstream plan Submit the plan action: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:caldiy:booking.create \ --action-id "$PLANACTIONID" \ --idempotency-key "$BOOKINGKEY" \ --transaction-mode plan \ --transaction-id "$TRANSACTIONID" \ --input @booking-create.json A successful result has validation class downstreamslotavailability and records sideeffectcommitted: false. It checks that the exact requested start appears in a one-day slot query. It does not reserve the slot. Read the durable transaction and copy the returned plan id exactly: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ transaction get "$AIPURL" \ --transaction-id "$TRANSACTIONID" \ --include-result \ --include-receipts export PLANID='paste-the-returned-plan-id' Do not infer a plan id from the transaction id. The implementation-generated plan expires after 15 minutes. Understand the reviewed plan limit The connector's create planner forwards an id selector and the requested start, but it does not forward teamSlug, lengthInMinutes, or nested attendee.timeZone into its slot probe. This guide avoids the first two fields; the attendee time zone remains required booking input but is not part of the reviewed provider slot query. Treat the plan as an exact-start observation, not full validation of team, custom duration, time-zone interpretation, attendee details, or provider acceptance. 5. Submit the matching commit Use a distinct commit action id, the same capability and input, the original transaction and idempotency key, and the returned plan id: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:caldiy:booking.create \ --action-id "$COMMITACTIONID" \ --idempotency-key "$BOOKINGKEY" \ --transaction-mode commit \ --transaction-id "$TRANSACTIONID" \ --plan-id "$PLANID" \ --input @booking-create.json The public commit path requires planid. The runtime rechecks the capability, input hash, planning principal, tenant and external account, transaction id, and plan expiry before it can claim the plan. Without an admitted approval decision, the commit action enters requireshuman. Store the returned approval id and leave the commit action in place. Do not submit a replacement commit. 6. Complete trusted approval Use the deployment's approver-facing process to inspect the frozen request and submit a decision from an authenticated principal with tenant-policy authority. The decision needs the request's policy hash and required reason, input snapshot, and policy-decision evidence. The Cal.diy approval validity window is 900,000 ms. The plan also expires after 15 minutes. If either boundary closes before commit ownership is claimed, stop and create a new plan for the current intent. Do not weaken policy or reuse an expired authorization. Approval resumes the same queued commit. It does not authorize a second action, prove that the plan is still valid, or prove that Cal.diy accepted the booking. 7. Wait for the durable result Read the original commit action independently of its submission connection: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action status "$AIPURL" \ "$COMMITACTIONID" \ --include-result \ --include-receipts \ --wait-ms 30000 Proceed only after a terminal completed result. Retain the provider request id and provider operation id when present. The common connector output requires status, but it does not guarantee a booking-id field. Validate a non-empty provider booking uid from the actual response contract before continuing. If the uid is absent, stop and treat the result as provider-contract drift. Do not derive a uid from the AIP action id, transaction id, or idempotency key. 8. Verify by reading the booking Write booking-get.json with the validated provider uid: { "bookinguid": "booking-provider-uid" } Read it under a new action id: export VERIFYACTIONID='actcalbookingverify001' aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:caldiy:booking.get \ --action-id "$VERIFYACTIONID" \ --mode sync \ --input @booking-get.json Compare only the intended start, event type, attendee, and status fields exposed by the provider response. Keep the complete input, output, approval evidence, and receipts under restricted PII controls. A completed create and matching read still do not prove that every calendar or notification was delivered. 9. Choose a follow-up operation Use the provider booking uid and select the smallest operation that matches the new intent: | Intent | Capability | New plan required | Important boundary | | Read current state | booking.get | No | Retry-safe read | | Move the start | booking.reschedule | Yes | New exact-start plan and approval | | Cancel | booking.cancel | Yes | Destructive; no rollback | | Confirm pending booking | booking.confirm | No | Approval and stable mutation key | | Decline pending booking | booking.decline | No | Destructive and approval-required | | Change location | booking.location.update | No | Read preflight; no empty update | | Add attendee or guests | booking.attendee.add, booking.guest.add | No | May affect calendars or notifications | | Remove attendee | booking.attendee.delete | No | Destructive; no compensation | | Change absence or owner | booking.markabsent, booking.reassign | No | High risk and approval-required | Every mutation needs a new action id and a stable idempotency key scoped to that exact intent. Reschedule and cancel need their own current plan; the create plan cannot authorize either change. Do not use the create capability's compensation declaration as ordinary cancellation. Compensation is a new, approval-required best-effort action against the original create and has a 24-hour contract window. It is neither automatic nor atomic. Recover without replaying a mutation | Observation | Response | | connector.caldiy.slotunavailable during plan | Choose a current start and create a new plan | | Plan expired or input hash changed | Create a new plan; do not alter the old commit | | Commit is requireshuman | Complete the existing approval workflow | | Commit is pending or in flight | Read the same action and transaction; wait | | connector.caldiy.idempotencycollision | Stop; the key already owns different input | | connector.caldiy.outcomeunknown or ambiguous transport failure | Reconcile the original provider operation | | Definitive provider rejection with no uncertain outcome | Correct the cause and begin a new governed intent | The CLI at this revision does not expose transaction mode reconcile, even though the protocol and runtime define it. Use the deployment's authorized reconciliation integration and retain the original action, transaction, idempotency, and provider-operation identities. Never replace the key to force another provider call. Verify the complete workflow Before treating the task as complete, confirm that you retained: • the catalog revision plus contract and schema digests; • slot-read action and exact selected start; • immutable booking input and its hash; • plan action, plan id, transaction id, and expiry; • approval id, policy hash, authority evidence, and terminal decision; • commit action, original idempotency key, durable result, and receipts; • provider request and operation ids when present; • validated provider booking uid and the follow-up read. This evidence demonstrates one governed application workflow. It does not by itself establish live-provider qualification, notification delivery, calendar delivery, availability guarantees, or production readiness. Related documentation • Booking capability reference (../capabilities/bookings.md) • Slots and reservations (../capabilities/slots-and-reservations.md) • Approvals and policy (../../../concepts/approvals-and-policy.md) • Transactions and compensation (../../../concepts/transactions-and-compensation.md) • Observe and recover (../../../guides/observe-and-recover.md)