Skip to content
AIPDocs
Release status
DocumentationAIP 1.0ConnectorConnectors

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 AIP_URL='https://aip.example.com'
export AIP_TOKEN_FILE='/run/secrets/aip-native-token'
export EVENT_TYPE_ID='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 "$AIP_TOKEN_FILE" \
  capability list "$AIP_URL" \
  --capability-id cap:cal_diy:slot.list \
  --limit 1

aipctl \
  --native-bearer-token-file "$AIP_TOKEN_FILE" \
  capability list "$AIP_URL" \
  --capability-id cap:cal_diy: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 SLOT_ACTION_ID='act_cal_booking_slots_001'

aipctl \
  --native-bearer-token-file "$AIP_TOKEN_FILE" \
  action call "$AIP_URL" \
  cap:cal_diy:slot.list \
  --action-id "$SLOT_ACTION_ID" \
  --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 PLAN_ACTION_ID='act_cal_booking_plan_001'
export COMMIT_ACTION_ID='act_cal_booking_commit_001'
export TRANSACTION_ID='txn_cal_booking_001'
export BOOKING_KEY='cal-booking-001'

Never reuse BOOKING_KEY with different canonical input or another external account.

4. Create the downstream plan

Submit the plan action:

aipctl \
  --native-bearer-token-file "$AIP_TOKEN_FILE" \
  action call "$AIP_URL" \
  cap:cal_diy:booking.create \
  --action-id "$PLAN_ACTION_ID" \
  --idempotency-key "$BOOKING_KEY" \
  --transaction-mode plan \
  --transaction-id "$TRANSACTION_ID" \
  --input @booking-create.json

A successful result has validation class downstream_slot_availability and records side_effect_committed: 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 "$AIP_TOKEN_FILE" \
  transaction get "$AIP_URL" \
  --transaction-id "$TRANSACTION_ID" \
  --include-result \
  --include-receipts

export PLAN_ID='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 "$AIP_TOKEN_FILE" \
  action call "$AIP_URL" \
  cap:cal_diy:booking.create \
  --action-id "$COMMIT_ACTION_ID" \
  --idempotency-key "$BOOKING_KEY" \
  --transaction-mode commit \
  --transaction-id "$TRANSACTION_ID" \
  --plan-id "$PLAN_ID" \
  --input @booking-create.json

The public commit path requires plan_id. 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 requires_human. 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 "$AIP_TOKEN_FILE" \
  action status "$AIP_URL" \
  "$COMMIT_ACTION_ID" \
  --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:

{
  "booking_uid": "booking-provider-uid"
}

Read it under a new action id:

export VERIFY_ACTION_ID='act_cal_booking_verify_001'

aipctl \
  --native-bearer-token-file "$AIP_TOKEN_FILE" \
  action call "$AIP_URL" \
  cap:cal_diy:booking.get \
  --action-id "$VERIFY_ACTION_ID" \
  --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.mark_absent, 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.cal_diy.slot_unavailable 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 requires_human Complete the existing approval workflow
Commit is pending or in flight Read the same action and transaction; wait
connector.cal_diy.idempotency_collision Stop; the key already owns different input
connector.cal_diy.outcome_unknown 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.