{
  "schemaVersion": "1.0",
  "title": "Cal.diy booking capabilities",
  "description": "Use these capabilities to find, create, reschedule, cancel, confirm, decline, mark, reassign, or relocate a Cal.diy booking. This page owns 12 core lifecycle operations. A separate capability page owns the 10 attendee, guest, calendar link,",
  "canonical": "https://getaip.org/docs/connectors/cal-diy/capabilities/bookings",
  "route": "/docs/connectors/cal-diy/capabilities/bookings",
  "source": "docs/connectors/cal-diy/capabilities/bookings.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/cal-diy/capabilities/bookings.md",
    "txt": "/docs/download/connectors/cal-diy/capabilities/bookings.txt",
    "json": "/docs/download/connectors/cal-diy/capabilities/bookings.json",
    "pdf": "/docs/download/connectors/cal-diy/capabilities/bookings.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Cal.diy booking capabilities\ndescription: Create and govern the core Cal.diy booking lifecycle through twelve exact operations\nkind: capability-reference\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: cal-diy\ncapabilityIds:\n  - cap:cal_diy:booking.list\n  - cap:cal_diy:booking.get\n  - cap:cal_diy:booking.get_by_seat\n  - cap:cal_diy:booking.create\n  - cap:cal_diy:booking.reschedule\n  - cap:cal_diy:booking.location.update\n  - cap:cal_diy:booking.cancel\n  - cap:cal_diy:booking.confirm\n  - cap:cal_diy:booking.decline\n  - cap:cal_diy:booking.mark_absent\n  - cap:cal_diy:booking.reassign\n  - cap:cal_diy:booking.reassign_to_user\n---\n\n# Cal.diy booking capabilities\n\nUse these capabilities to find, create, reschedule, cancel, confirm, decline,\nmark, reassign, or relocate a Cal.diy booking. This page owns 12 core lifecycle\noperations. A separate capability page owns the 10 attendee, guest, calendar\nlink, reference, recording, transcript, and conferencing-session operations.\n\nAll booking operations use provider API version `2024-08-13`. Booking data is\nrestricted, contains PII, and requires redaction throughout this family.\n\n## Choose an operation\n\n### Find a booking\n\n| Capability | Provider request | Risk | Approval |\n|---|---|---|---|\n| `cap:cal_diy:booking.list` | `GET /v2/bookings` | Low | No |\n| `cap:cal_diy:booking.get` | `GET /v2/bookings/{booking_uid}` | Low | No |\n| `cap:cal_diy:booking.get_by_seat` | `GET /v2/bookings/by-seat/{seat_uid}` | Low | No |\n\n### Create or move a booking\n\n| Capability | Provider request | Risk | Plan required |\n|---|---|---|---|\n| `cap:cal_diy:booking.create` | `POST /v2/bookings` | Medium | Yes |\n| `cap:cal_diy:booking.reschedule` | `POST /v2/bookings/{booking_uid}/reschedule` | High | Yes |\n| `cap:cal_diy:booking.location.update` | `PATCH /v2/bookings/{booking_uid}/location` | Medium | No |\n\n### Change booking state or ownership\n\n| Capability | Provider request | Risk | Plan required |\n|---|---|---|---|\n| `cap:cal_diy:booking.cancel` | `POST /v2/bookings/{booking_uid}/cancel` | High | Yes |\n| `cap:cal_diy:booking.confirm` | `POST /v2/bookings/{booking_uid}/confirm` | High | No |\n| `cap:cal_diy:booking.decline` | `POST /v2/bookings/{booking_uid}/decline` | High | No |\n| `cap:cal_diy:booking.mark_absent` | `POST /v2/bookings/{booking_uid}/mark-absent` | High | No |\n| `cap:cal_diy:booking.reassign` | `POST /v2/bookings/{booking_uid}/reassign` | High | No |\n| `cap:cal_diy:booking.reassign_to_user` | `POST /v2/bookings/{booking_uid}/reassign/{user_id}` | High | No |\n\nEvery mutation requires approval and an idempotency key even when the table\ndoes not require a transaction plan.\n\n## List bookings\n\n`cap:cal_diy:booking.list` accepts an empty object or any supported filter:\n\n| Field | Constraint |\n|---|---|\n| `status` | Non-empty unique array of `upcoming`, `recurring`, `past`, `cancelled`, or `unconfirmed` |\n| `attendeeEmail` | Email string |\n| `attendeeName` | String |\n| `bookingUid` | Non-empty string |\n| `eventTypeIds` | Non-empty array of integers |\n| `eventTypeId` | Integer |\n| `teamsIds` | Non-empty array of integers |\n| `teamId` | Integer |\n| `afterStart`, `beforeEnd` | Date-time strings |\n| `afterCreatedAt`, `beforeCreatedAt` | Date-time strings |\n| `afterUpdatedAt`, `beforeUpdatedAt` | Date-time strings |\n| `sortStart`, `sortEnd` | `asc` or `desc` |\n| `sortCreated`, `sortUpdatedAt` | `asc` or `desc` |\n| `take` | Integer from `1` through `250` |\n| `skip` | Integer at least `0` |\n\nAdditional fields are rejected. The connector encodes these values as query\nparameters. It does not add logical constraints between overlapping singular\nand plural filters; Cal.diy can reject or further interpret a schema-valid\ncombination.\n\nExample:\n\n```json\n{\n  \"status\": [\"upcoming\", \"unconfirmed\"],\n  \"afterStart\": \"2030-01-01T00:00:00Z\",\n  \"beforeEnd\": \"2030-02-01T00:00:00Z\",\n  \"sortStart\": \"asc\",\n  \"take\": 50,\n  \"skip\": 0\n}\n```\n\n## Read by booking or seat uid\n\nRead a regular or recurring booking set with:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\"\n}\n```\n\nRead a seated booking with:\n\n```json\n{\n  \"seat_uid\": \"seat-provider-uid\"\n}\n```\n\nBoth `booking_uid` and `seat_uid` must be non-empty strings. Treat them as\nsensitive external handles. The connector percent-encodes them into provider\npaths, so keep raw values out of application logs, user-visible URLs, and\nmetric labels.\n\nThe three read capabilities are synchronous, low risk, retry-safe, and eligible\nfor connector retry. Their idempotency key is optional and action-scoped. They\nstill require authenticated account routing and restricted-data policy.\n\n## Create a booking\n\n`cap:cal_diy:booking.create` requires `start`, `attendee`, and exactly one\nevent-type selector. A minimal id-based input is:\n\n```json\n{\n  \"start\": \"2030-01-03T10:00:00Z\",\n  \"eventTypeId\": 42,\n  \"attendee\": {\n    \"name\": \"Avery Patel\",\n    \"email\": \"avery@example.com\",\n    \"timeZone\": \"Europe/London\"\n  }\n}\n```\n\n### Event-type selectors\n\nChoose exactly one branch:\n\n| Branch | Required fields | Optional related field |\n|---|---|---|\n| Provider id | `eventTypeId` | None |\n| User-owned slug | `eventTypeSlug`, `username` | `organizationSlug` |\n| Team-owned slug | `eventTypeSlug`, `teamSlug` | `organizationSlug` |\n\nThe id is an integer. Slugs and usernames must be non-empty strings. Satisfying\nmore than one branch makes the `oneOf` input invalid. Do not mix selector\nfields even when an incomplete second branch could still pass the schema.\n\n### Attendee\n\n`attendee` requires a non-empty `name`, a non-empty `timeZone`, and at least one\nof `email` or `phoneNumber`:\n\n| Field | Constraint |\n|---|---|\n| `name` | Non-empty string |\n| `timeZone` | Non-empty string |\n| `email` | Email string; required when phone is absent |\n| `phoneNumber` | `+` plus 7 through 15 digits; required when email is absent |\n| `language` | The 43-value Cal.diy locale enum |\n\nThe locale enum is the same source-owned set listed in the\n[profile capability reference](profile.md). Additional attendee fields are\nrejected.\n\n### Optional booking fields\n\n| Field | Constraint or role |\n|---|---|\n| `bookingFieldsResponses` | Object with provider-defined response keys and values |\n| `guests` | Unique array of email strings |\n| `meetingUrl` | URI string; deprecated |\n| `location` | One supported create-location variant |\n| `metadata` | At most 46 application entries; string values only |\n| `lengthInMinutes` | Integer at least `1` |\n| `routing` | Routing-form response and team-member selection |\n| `emailVerificationCode` | String |\n| `recurrenceCount` | Integer at least `1` |\n\n`metadata` property names have at most 40 characters and values have at most\n500 characters. The connector reserves and writes these keys before provider\ndispatch:\n\n- `aip_action_id`;\n- `aip_operation_id`;\n- `aip_idempotency_hash`;\n- `aip_transaction_id` when a transaction id exists.\n\nThe idempotency value is hashed before insertion. Do not supply those reserved\nkeys as application metadata; connector values overwrite matching keys.\n\n`routing` requires integer `responseId` and an array of integer\n`teamMemberIds`. It may also contain `teamMemberEmail`, `skipContactOwner`,\n`crmAppSlug`, and `crmOwnerRecordType`. Additional routing fields are rejected.\n\n### Create locations\n\nCreate accepts these object variants:\n\n| Type | Required value |\n|---|---|\n| `integration` | One of the 29 supported integration names |\n| `attendeeAddress` | Non-empty `address` |\n| `attendeePhone` | E.164-like `phone` |\n| `attendeeDefined` | Non-empty `location` |\n| `address`, `link`, `phone`, `organizersDefaultApp` | Type discriminator only |\n\nThe integration enum is listed in the\n[event-type capability reference](event-types-and-private-links.md). A\nnon-empty free-form string is also accepted for compatibility but is marked\ndeprecated. Do not introduce new use of the deprecated string or `meetingUrl`.\n\n## Plan booking creation\n\nBooking create cannot use the direct invocation path. The connector returns\n`connector.cal_diy.transaction_required` until the action follows the AIP plan\nand commit lifecycle.\n\nThe plan:\n\n1. validates the exact create input;\n2. queries `slot.list` from the requested start through one day later;\n3. requests `time` format;\n4. verifies that the response contains the exact requested date-time start;\n5. returns a completed plan result without committing a provider side effect.\n\nThe plan result identifies the operation, API version, provider path, input\nhash, validation class `downstream_slot_availability`, and\n`side_effect_committed: false`.\n\nA passing plan is an observation, not a reservation. Availability can change\nbefore commit.\n\n### Reviewed planner limitation\n\nAt the reviewed source revision, the slot probe forwards `eventTypeId`,\n`eventTypeSlug`, `username`, `organizationSlug`, `timeZone`, and a field named\n`duration` when present. The create schema exposes `teamSlug`, nested\n`attendee.timeZone`, and `lengthInMinutes`; it does not accept top-level\n`timeZone` or `duration`.\n\nConsequently, the planner does not forward a team selector's `teamSlug` or a\ncustom `lengthInMinutes` or attendee time zone into the provider availability\nquery. It checks only whether the requested start appears in the returned\nslots. Treat team-slug, custom-length, and time-zone plan coverage as an\nimplementation limitation at this revision; do not describe it as full\ndownstream validation.\n\n## Reschedule a booking\n\nReschedule requires a booking uid and new start:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"start\": \"2030-01-03T11:00:00Z\",\n  \"rescheduledBy\": \"operator@example.com\",\n  \"reschedulingReason\": \"Customer requested a later time\"\n}\n```\n\nOptional fields are `rescheduledBy` as an email, `reschedulingReason` as a\nstring, non-empty `seatUid`, and `emailVerificationCode` as a string.\n\nFor a seat-specific reschedule, include `seatUid` and omit\n`reschedulingReason`. The schema rejects an input containing both. A regular\nreschedule omits `seatUid` and may include the reason.\n\nReschedule is high risk and requires plan before commit. Its plan reads the\nbooking, requires integer `/data/eventTypeId` in the provider result, queries\navailability for that event type while passing the booking uid as\n`bookingUidToReschedule`, and checks the exact requested start. The plan's\nvalidation class is `booking_state_and_downstream_slot_availability`.\n\nIf the booking result omits `data.eventTypeId`, planning fails before the\nreschedule request. A passing plan still does not reserve the new slot.\n\n## Cancel a booking\n\nCancel requires the booking uid:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"cancellationReason\": \"Customer cancelled\",\n  \"cancelSubsequentBookings\": true\n}\n```\n\nFor a seat-specific cancellation, include non-empty `seatUid` and omit\n`cancelSubsequentBookings`. The schema rejects both fields together.\n`cancellationReason` remains optional in either branch.\n\nCancel is a high-risk destructive mutation and requires plan before commit.\nIts plan reads the target booking and returns validation class\n`downstream_target_exists`. It does not simulate cancellation or prove that\nnotifications will be delivered.\n\nCancellation has no rollback. It is the named best-effort compensation for a\nbooking create, but executing it cannot erase already delivered notifications,\ncalendar observations, or other downstream effects.\n\n## Confirm or decline a booking\n\nConfirm accepts only:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\"\n}\n```\n\nDecline optionally adds a reason:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"reason\": \"Requested time cannot be supported\"\n}\n```\n\nBoth are high risk. Confirm is not marked destructive; decline is. Neither\nrequires a plan before commit, but both require approval and first read the\ntarget booking. Both carry the `send_message` side-effect marker.\n\n## Mark absence\n\nMark host absence, attendee absence, or both:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"host\": false,\n  \"attendees\": [\n    {\n      \"email\": \"avery@example.com\",\n      \"absent\": true\n    }\n  ]\n}\n```\n\n`attendees` must be non-empty when present. Each item requires an email and a\nBoolean `absent`; additional item fields are rejected.\n\nOnly `booking_uid` is required by the schema, so an input with no `host` or\n`attendees` is schema-valid. Do not use an empty absence update as a probe;\nCal.diy may reject it or apply provider-specific behavior.\n\nThis high-risk mutation first reads the booking. It does not carry the\nconnector's `send_message` marker.\n\n## Reassign a booking\n\nAutomatic round-robin reassignment accepts only the booking uid:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\"\n}\n```\n\nReassignment to a selected user also requires an integer `user_id` and may\ninclude a reason:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"user_id\": 73,\n  \"reason\": \"Route to the account owner\"\n}\n```\n\nThe connector puts both identifiers in the provider path and sends `reason` in\nthe JSON body. Both operations are high risk, first read the booking, and do\nnot carry the connector's `send_message` marker. Provider behavior may still\naffect calendars or notifications; the absence of that marker is not a\ndelivery guarantee.\n\n## Update the booking location\n\nLocation update requires only the booking uid; include a location to make an\nintentional change:\n\n```json\n{\n  \"booking_uid\": \"booking-provider-uid\",\n  \"location\": {\n    \"type\": \"link\",\n    \"link\": \"https://meet.example.com/room-42\"\n  }\n}\n```\n\nUpdate accepts:\n\n- `integration` with one supported integration name;\n- `attendeeAddress` with non-empty `address`;\n- `attendeePhone` with E.164-like `phone`;\n- `attendeeDefined` with non-empty `location`;\n- `address`, `link`, or `phone` with the corresponding non-empty value.\n\nUnlike create, location update does not accept `organizersDefaultApp`, a bare\ndeprecated string, or discriminator-only `address`, `link`, or `phone`.\n\nThe schema permits omission of `location`. Do not send that empty update as a\nprobe. This medium-risk mutation first reads the booking and does not carry the\n`send_message` marker.\n\n## Preflight and planning matrix\n\n| Mutation | Read or validation before provider mutation | Plan required |\n|---|---|---|\n| Create | Exact-start slot query | Yes |\n| Reschedule | Booking read, event-type extraction, exact-start slot query | Yes |\n| Cancel | Booking read | Yes |\n| Confirm | Booking read | No |\n| Decline | Booking read | No |\n| Mark absent | Booking read | No |\n| Reassign | Booking read | No |\n| Reassign to user | Booking read | No |\n| Location update | Booking read | No |\n\nA preflight detects some stale or missing targets before mutation. It does not\nreserve state, authorize the action, guarantee provider acceptance, or make a\nwrite safe to retry.\n\n## Shared mutation contract\n\nThe nine mutations require:\n\n- approval selected by tenant policy;\n- a 900,000 ms approval validity window;\n- reason, input snapshot, and policy-decision evidence;\n- an external-account-scoped idempotency key;\n- input-hash revalidation on key collision.\n\nAll mutations are synchronous, advertise no connector retry, and are retry\nunsafe. Preserve the same key only for the same capability, account, and\ncanonical input. Reconcile uncertain provider dispatch instead of creating a\nreplacement key.\n\nCreate, reschedule, cancel, confirm, and decline carry `send_message` in\naddition to write and external-network side effects. This marker means a\nprovider operation may affect customer-visible notifications or connected\ncalendar state; it does not prove delivery.\n\nAll mutations advertise `dry_run`, `plan`, `commit`, and `reconcile`. Create,\nreschedule, and cancel require a validated plan and have downstream-validation\ndry-run fidelity. The remaining mutations have policy-and-schema fidelity and\ndo not require plan before commit.\n\nBooking create names `booking.cancel` as best-effort compensation. The contract\nwindow is 24 hours and compensation requires approval. It is a new governed\naction, not an automatic or atomic rollback. Every other operation on this page\nhas no compensating capability.\n\n## Result and data contract\n\nThe connector returns the validated provider JSON body as AIP output. It\nrequires a non-empty string `status`; `data`, `pagination`, and additional\nprovider fields are optional. The connector does not publish an exhaustive\nbooking response schema.\n\nBefore returning output, the connector recursively redacts fields whose\nnormalized names identify secrets, API keys, or tokens. This targeted redaction\ndoes not declassify booking content. Inputs, outputs, approval snapshots,\nreceipts, and diagnostic evidence remain restricted and PII-bearing.\n\n## Failures and recovery\n\n| Failure | Relevant cause | Safe response |\n|---|---|---|\n| `connector.cal_diy.transaction_required` | Create, reschedule, or cancel used direct invocation | Start the governed plan-and-commit lifecycle |\n| `connector.cal_diy.slot_unavailable` | Planned create or reschedule start is absent from current slots | Choose a new start and produce a new plan |\n| `connector.cal_diy.invalid_action` | Schema mismatch or reschedule booking result omitted `eventTypeId` | Correct input or investigate provider response drift |\n| `connector.cal_diy.policy` | Mutation omitted its idempotency key | Preserve approved input and supply the required key |\n| `connector.cal_diy.idempotency_collision` | One key owns different canonical input | Stop and inspect the original action |\n| `connector.cal_diy.in_flight` | The matching mutation is executing | Read durable state and wait |\n| `connector.cal_diy.outcome_unknown` | Prior provider dispatch cannot be proved | Reconcile; do not repeat the mutation |\n| `connector.cal_diy.authentication` | Provider returned `401` or `403` | Verify account binding, credential revision, and booking ownership |\n| `connector.cal_diy.state_conflict` | Provider returned `409` or `412` | Read current booking state and reconsider the transition |\n| `connector.cal_diy.rate_limited` | Provider returned `429` | Retry reads within policy; reconcile mutations |\n| `connector.cal_diy.remote_temporary` | Provider returned a server error | Retry reads within policy; reconcile mutations |\n| `connector.cal_diy.transport` | Provider exchange failed | Retry reads only; treat dispatched mutations as uncertain |\n| `connector.cal_diy.invalid_provider_output` | Provider success lacked a valid `status` | Preserve evidence and reconcile a mutation |\n| `connector.cal_diy.reconciliation_not_found` | Durable provider operation cannot be found | Stop and escalate with the original action and operation ids |\n\nPermanent provider rejection, invalid credentials, oversized responses, and\ndurable settlement failures use the common Cal.diy error family. Gateway and\nlifecycle errors are described in the [global error reference](../../../reference/errors.md).\n\n## Verify a booking transition\n\nFor every operation, retain the capability id, tenant and external account,\nAIP action id, input hash, provider request id when present, and completed or\nreconciled durable state.\n\nFor create, reschedule, or cancel, additionally verify the plan id and the\nexact input bound to it. For create, confirm the provider booking uid before\nusing it in later actions. For state transitions, read the booking after a\ncompleted result and compare only the intended fields.\n\nDo not use a follow-up read to justify replaying an uncertain mutation. Resolve\nthe original provider operation through reconciliation and retain the evidence\nstrength of that result.\n\nThese checks validate application handling. They do not establish live Cal.diy\nqualification or production readiness for a specific deployment.\n\n## Related documentation\n\n- [Cal.diy capability index](README.md)\n- [Slots and reservations](slots-and-reservations.md)\n- [Booking attendees and artifacts](booking-attendees-and-artifacts.md)\n- [Approvals and policy](../../../concepts/approvals-and-policy.md)\n- [Transactions and compensation](../../../concepts/transactions-and-compensation.md)\n",
    "text": "Cal.diy booking capabilities\n\nUse these capabilities to find, create, reschedule, cancel, confirm, decline,\nmark, reassign, or relocate a Cal.diy booking. This page owns 12 core lifecycle\noperations. A separate capability page owns the 10 attendee, guest, calendar\nlink, reference, recording, transcript, and conferencing-session operations.\n\nAll booking operations use provider API version 2024-08-13. Booking data is\nrestricted, contains PII, and requires redaction throughout this family.\n\nChoose an operation\n\nFind a booking\n\n| Capability | Provider request | Risk | Approval |\n\n| cap:caldiy:booking.list | GET /v2/bookings | Low | No |\n| cap:caldiy:booking.get | GET /v2/bookings/{bookinguid} | Low | No |\n| cap:caldiy:booking.getbyseat | GET /v2/bookings/by-seat/{seatuid} | Low | No |\n\nCreate or move a booking\n\n| Capability | Provider request | Risk | Plan required |\n\n| cap:caldiy:booking.create | POST /v2/bookings | Medium | Yes |\n| cap:caldiy:booking.reschedule | POST /v2/bookings/{bookinguid}/reschedule | High | Yes |\n| cap:caldiy:booking.location.update | PATCH /v2/bookings/{bookinguid}/location | Medium | No |\n\nChange booking state or ownership\n\n| Capability | Provider request | Risk | Plan required |\n\n| cap:caldiy:booking.cancel | POST /v2/bookings/{bookinguid}/cancel | High | Yes |\n| cap:caldiy:booking.confirm | POST /v2/bookings/{bookinguid}/confirm | High | No |\n| cap:caldiy:booking.decline | POST /v2/bookings/{bookinguid}/decline | High | No |\n| cap:caldiy:booking.markabsent | POST /v2/bookings/{bookinguid}/mark-absent | High | No |\n| cap:caldiy:booking.reassign | POST /v2/bookings/{bookinguid}/reassign | High | No |\n| cap:caldiy:booking.reassigntouser | POST /v2/bookings/{bookinguid}/reassign/{userid} | High | No |\n\nEvery mutation requires approval and an idempotency key even when the table\ndoes not require a transaction plan.\n\nList bookings\n\ncap:caldiy:booking.list accepts an empty object or any supported filter:\n\n| Field | Constraint |\n\n| status | Non-empty unique array of upcoming, recurring, past, cancelled, or unconfirmed |\n| attendeeEmail | Email string |\n| attendeeName | String |\n| bookingUid | Non-empty string |\n| eventTypeIds | Non-empty array of integers |\n| eventTypeId | Integer |\n| teamsIds | Non-empty array of integers |\n| teamId | Integer |\n| afterStart, beforeEnd | Date-time strings |\n| afterCreatedAt, beforeCreatedAt | Date-time strings |\n| afterUpdatedAt, beforeUpdatedAt | Date-time strings |\n| sortStart, sortEnd | asc or desc |\n| sortCreated, sortUpdatedAt | asc or desc |\n| take | Integer from 1 through 250 |\n| skip | Integer at least 0 |\n\nAdditional fields are rejected. The connector encodes these values as query\nparameters. It does not add logical constraints between overlapping singular\nand plural filters; Cal.diy can reject or further interpret a schema-valid\ncombination.\n\nExample:\n\n{\n  \"status\": [\"upcoming\", \"unconfirmed\"],\n  \"afterStart\": \"2030-01-01T00:00:00Z\",\n  \"beforeEnd\": \"2030-02-01T00:00:00Z\",\n  \"sortStart\": \"asc\",\n  \"take\": 50,\n  \"skip\": 0\n}\n\nRead by booking or seat uid\n\nRead a regular or recurring booking set with:\n\n{\n  \"bookinguid\": \"booking-provider-uid\"\n}\n\nRead a seated booking with:\n\n{\n  \"seatuid\": \"seat-provider-uid\"\n}\n\nBoth bookinguid and seatuid must be non-empty strings. Treat them as\nsensitive external handles. The connector percent-encodes them into provider\npaths, so keep raw values out of application logs, user-visible URLs, and\nmetric labels.\n\nThe three read capabilities are synchronous, low risk, retry-safe, and eligible\nfor connector retry. Their idempotency key is optional and action-scoped. They\nstill require authenticated account routing and restricted-data policy.\n\nCreate a booking\n\ncap:caldiy:booking.create requires start, attendee, and exactly one\nevent-type selector. A minimal id-based input is:\n\n{\n  \"start\": \"2030-01-03T10:00:00Z\",\n  \"eventTypeId\": 42,\n  \"attendee\": {\n    \"name\": \"Avery Patel\",\n    \"email\": \"avery@example.com\",\n    \"timeZone\": \"Europe/London\"\n  }\n}\n\nEvent-type selectors\n\nChoose exactly one branch:\n\n| Branch | Required fields | Optional related field |\n\n| Provider id | eventTypeId | None |\n| User-owned slug | eventTypeSlug, username | organizationSlug |\n| Team-owned slug | eventTypeSlug, teamSlug | organizationSlug |\n\nThe id is an integer. Slugs and usernames must be non-empty strings. Satisfying\nmore than one branch makes the oneOf input invalid. Do not mix selector\nfields even when an incomplete second branch could still pass the schema.\n\nAttendee\n\nattendee requires a non-empty name, a non-empty timeZone, and at least one\nof email or phoneNumber:\n\n| Field | Constraint |\n\n| name | Non-empty string |\n| timeZone | Non-empty string |\n| email | Email string; required when phone is absent |\n| phoneNumber | + plus 7 through 15 digits; required when email is absent |\n| language | The 43-value Cal.diy locale enum |\n\nThe locale enum is the same source-owned set listed in the\nprofile capability reference (profile.md). Additional attendee fields are\nrejected.\n\nOptional booking fields\n\n| Field | Constraint or role |\n\n| bookingFieldsResponses | Object with provider-defined response keys and values |\n| guests | Unique array of email strings |\n| meetingUrl | URI string; deprecated |\n| location | One supported create-location variant |\n| metadata | At most 46 application entries; string values only |\n| lengthInMinutes | Integer at least 1 |\n| routing | Routing-form response and team-member selection |\n| emailVerificationCode | String |\n| recurrenceCount | Integer at least 1 |\n\nmetadata property names have at most 40 characters and values have at most\n500 characters. The connector reserves and writes these keys before provider\ndispatch:\n• aipactionid;\n• aipoperationid;\n• aipidempotencyhash;\n• aiptransactionid when a transaction id exists.\n\nThe idempotency value is hashed before insertion. Do not supply those reserved\nkeys as application metadata; connector values overwrite matching keys.\n\nrouting requires integer responseId and an array of integer\nteamMemberIds. It may also contain teamMemberEmail, skipContactOwner,\ncrmAppSlug, and crmOwnerRecordType. Additional routing fields are rejected.\n\nCreate locations\n\nCreate accepts these object variants:\n\n| Type | Required value |\n\n| integration | One of the 29 supported integration names |\n| attendeeAddress | Non-empty address |\n| attendeePhone | E.164-like phone |\n| attendeeDefined | Non-empty location |\n| address, link, phone, organizersDefaultApp | Type discriminator only |\n\nThe integration enum is listed in the\nevent-type capability reference (event-types-and-private-links.md). A\nnon-empty free-form string is also accepted for compatibility but is marked\ndeprecated. Do not introduce new use of the deprecated string or meetingUrl.\n\nPlan booking creation\n\nBooking create cannot use the direct invocation path. The connector returns\nconnector.caldiy.transactionrequired until the action follows the AIP plan\nand commit lifecycle.\n\nThe plan:\n1. validates the exact create input;\n2. queries slot.list from the requested start through one day later;\n3. requests time format;\n4. verifies that the response contains the exact requested date-time start;\n5. returns a completed plan result without committing a provider side effect.\n\nThe plan result identifies the operation, API version, provider path, input\nhash, validation class downstreamslotavailability, and\nsideeffectcommitted: false.\n\nA passing plan is an observation, not a reservation. Availability can change\nbefore commit.\n\nReviewed planner limitation\n\nAt the reviewed source revision, the slot probe forwards eventTypeId,\neventTypeSlug, username, organizationSlug, timeZone, and a field named\nduration when present. The create schema exposes teamSlug, nested\nattendee.timeZone, and lengthInMinutes; it does not accept top-level\ntimeZone or duration.\n\nConsequently, the planner does not forward a team selector's teamSlug or a\ncustom lengthInMinutes or attendee time zone into the provider availability\nquery. It checks only whether the requested start appears in the returned\nslots. Treat team-slug, custom-length, and time-zone plan coverage as an\nimplementation limitation at this revision; do not describe it as full\ndownstream validation.\n\nReschedule a booking\n\nReschedule requires a booking uid and new start:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"start\": \"2030-01-03T11:00:00Z\",\n  \"rescheduledBy\": \"operator@example.com\",\n  \"reschedulingReason\": \"Customer requested a later time\"\n}\n\nOptional fields are rescheduledBy as an email, reschedulingReason as a\nstring, non-empty seatUid, and emailVerificationCode as a string.\n\nFor a seat-specific reschedule, include seatUid and omit\nreschedulingReason. The schema rejects an input containing both. A regular\nreschedule omits seatUid and may include the reason.\n\nReschedule is high risk and requires plan before commit. Its plan reads the\nbooking, requires integer /data/eventTypeId in the provider result, queries\navailability for that event type while passing the booking uid as\nbookingUidToReschedule, and checks the exact requested start. The plan's\nvalidation class is bookingstateanddownstreamslotavailability.\n\nIf the booking result omits data.eventTypeId, planning fails before the\nreschedule request. A passing plan still does not reserve the new slot.\n\nCancel a booking\n\nCancel requires the booking uid:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"cancellationReason\": \"Customer cancelled\",\n  \"cancelSubsequentBookings\": true\n}\n\nFor a seat-specific cancellation, include non-empty seatUid and omit\ncancelSubsequentBookings. The schema rejects both fields together.\ncancellationReason remains optional in either branch.\n\nCancel is a high-risk destructive mutation and requires plan before commit.\nIts plan reads the target booking and returns validation class\ndownstreamtargetexists. It does not simulate cancellation or prove that\nnotifications will be delivered.\n\nCancellation has no rollback. It is the named best-effort compensation for a\nbooking create, but executing it cannot erase already delivered notifications,\ncalendar observations, or other downstream effects.\n\nConfirm or decline a booking\n\nConfirm accepts only:\n\n{\n  \"bookinguid\": \"booking-provider-uid\"\n}\n\nDecline optionally adds a reason:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"reason\": \"Requested time cannot be supported\"\n}\n\nBoth are high risk. Confirm is not marked destructive; decline is. Neither\nrequires a plan before commit, but both require approval and first read the\ntarget booking. Both carry the sendmessage side-effect marker.\n\nMark absence\n\nMark host absence, attendee absence, or both:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"host\": false,\n  \"attendees\": [\n    {\n      \"email\": \"avery@example.com\",\n      \"absent\": true\n    }\n  ]\n}\n\nattendees must be non-empty when present. Each item requires an email and a\nBoolean absent; additional item fields are rejected.\n\nOnly bookinguid is required by the schema, so an input with no host or\nattendees is schema-valid. Do not use an empty absence update as a probe;\nCal.diy may reject it or apply provider-specific behavior.\n\nThis high-risk mutation first reads the booking. It does not carry the\nconnector's sendmessage marker.\n\nReassign a booking\n\nAutomatic round-robin reassignment accepts only the booking uid:\n\n{\n  \"bookinguid\": \"booking-provider-uid\"\n}\n\nReassignment to a selected user also requires an integer userid and may\ninclude a reason:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"userid\": 73,\n  \"reason\": \"Route to the account owner\"\n}\n\nThe connector puts both identifiers in the provider path and sends reason in\nthe JSON body. Both operations are high risk, first read the booking, and do\nnot carry the connector's sendmessage marker. Provider behavior may still\naffect calendars or notifications; the absence of that marker is not a\ndelivery guarantee.\n\nUpdate the booking location\n\nLocation update requires only the booking uid; include a location to make an\nintentional change:\n\n{\n  \"bookinguid\": \"booking-provider-uid\",\n  \"location\": {\n    \"type\": \"link\",\n    \"link\": \"https://meet.example.com/room-42\"\n  }\n}\n\nUpdate accepts:\n• integration with one supported integration name;\n• attendeeAddress with non-empty address;\n• attendeePhone with E.164-like phone;\n• attendeeDefined with non-empty location;\n• address, link, or phone with the corresponding non-empty value.\n\nUnlike create, location update does not accept organizersDefaultApp, a bare\ndeprecated string, or discriminator-only address, link, or phone.\n\nThe schema permits omission of location. Do not send that empty update as a\nprobe. This medium-risk mutation first reads the booking and does not carry the\nsendmessage marker.\n\nPreflight and planning matrix\n\n| Mutation | Read or validation before provider mutation | Plan required |\n\n| Create | Exact-start slot query | Yes |\n| Reschedule | Booking read, event-type extraction, exact-start slot query | Yes |\n| Cancel | Booking read | Yes |\n| Confirm | Booking read | No |\n| Decline | Booking read | No |\n| Mark absent | Booking read | No |\n| Reassign | Booking read | No |\n| Reassign to user | Booking read | No |\n| Location update | Booking read | No |\n\nA preflight detects some stale or missing targets before mutation. It does not\nreserve state, authorize the action, guarantee provider acceptance, or make a\nwrite safe to retry.\n\nShared mutation contract\n\nThe nine mutations require:\n• approval selected by tenant policy;\n• a 900,000 ms approval validity window;\n• reason, input snapshot, and policy-decision evidence;\n• an external-account-scoped idempotency key;\n• input-hash revalidation on key collision.\n\nAll mutations are synchronous, advertise no connector retry, and are retry\nunsafe. Preserve the same key only for the same capability, account, and\ncanonical input. Reconcile uncertain provider dispatch instead of creating a\nreplacement key.\n\nCreate, reschedule, cancel, confirm, and decline carry sendmessage in\naddition to write and external-network side effects. This marker means a\nprovider operation may affect customer-visible notifications or connected\ncalendar state; it does not prove delivery.\n\nAll mutations advertise dryrun, plan, commit, and reconcile. Create,\nreschedule, and cancel require a validated plan and have downstream-validation\ndry-run fidelity. The remaining mutations have policy-and-schema fidelity and\ndo not require plan before commit.\n\nBooking create names booking.cancel as best-effort compensation. The contract\nwindow is 24 hours and compensation requires approval. It is a new governed\naction, not an automatic or atomic rollback. Every other operation on this page\nhas no compensating capability.\n\nResult and data contract\n\nThe connector returns the validated provider JSON body as AIP output. It\nrequires a non-empty string status; data, pagination, and additional\nprovider fields are optional. The connector does not publish an exhaustive\nbooking response schema.\n\nBefore returning output, the connector recursively redacts fields whose\nnormalized names identify secrets, API keys, or tokens. This targeted redaction\ndoes not declassify booking content. Inputs, outputs, approval snapshots,\nreceipts, and diagnostic evidence remain restricted and PII-bearing.\n\nFailures and recovery\n\n| Failure | Relevant cause | Safe response |\n\n| connector.caldiy.transactionrequired | Create, reschedule, or cancel used direct invocation | Start the governed plan-and-commit lifecycle |\n| connector.caldiy.slotunavailable | Planned create or reschedule start is absent from current slots | Choose a new start and produce a new plan |\n| connector.caldiy.invalidaction | Schema mismatch or reschedule booking result omitted eventTypeId | Correct input or investigate provider response drift |\n| connector.caldiy.policy | Mutation omitted its idempotency key | Preserve approved input and supply the required key |\n| connector.caldiy.idempotencycollision | One key owns different canonical input | Stop and inspect the original action |\n| connector.caldiy.inflight | The matching mutation is executing | Read durable state and wait |\n| connector.caldiy.outcomeunknown | Prior provider dispatch cannot be proved | Reconcile; do not repeat the mutation |\n| connector.caldiy.authentication | Provider returned 401 or 403 | Verify account binding, credential revision, and booking ownership |\n| connector.caldiy.stateconflict | Provider returned 409 or 412 | Read current booking state and reconsider the transition |\n| connector.caldiy.ratelimited | Provider returned 429 | Retry reads within policy; reconcile mutations |\n| connector.caldiy.remotetemporary | Provider returned a server error | Retry reads within policy; reconcile mutations |\n| connector.caldiy.transport | Provider exchange failed | Retry reads only; treat dispatched mutations as uncertain |\n| connector.caldiy.invalidprovideroutput | Provider success lacked a valid status | Preserve evidence and reconcile a mutation |\n| connector.caldiy.reconciliationnotfound | Durable provider operation cannot be found | Stop and escalate with the original action and operation ids |\n\nPermanent provider rejection, invalid credentials, oversized responses, and\ndurable settlement failures use the common Cal.diy error family. Gateway and\nlifecycle errors are described in the global error reference (../../../reference/errors.md).\n\nVerify a booking transition\n\nFor every operation, retain the capability id, tenant and external account,\nAIP action id, input hash, provider request id when present, and completed or\nreconciled durable state.\n\nFor create, reschedule, or cancel, additionally verify the plan id and the\nexact input bound to it. For create, confirm the provider booking uid before\nusing it in later actions. For state transitions, read the booking after a\ncompleted result and compare only the intended fields.\n\nDo not use a follow-up read to justify replaying an uncertain mutation. Resolve\nthe original provider operation through reconciliation and retain the evidence\nstrength of that result.\n\nThese checks validate application handling. They do not establish live Cal.diy\nqualification or production readiness for a specific deployment.\n\nRelated documentation\n• Cal.diy capability index (README.md)\n• Slots and reservations (slots-and-reservations.md)\n• Booking attendees and artifacts (booking-attendees-and-artifacts.md)\n• Approvals and policy (../../../concepts/approvals-and-policy.md)\n• Transactions and compensation (../../../concepts/transactions-and-compensation.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "cc3c00d574751101f237ba63a269a0b3268e4cf9991131b72de2589bb93507ef"
  }
}
