{
  "schemaVersion": "1.0",
  "title": "Cal.diy event types, private links, and webhooks",
  "description": "Use this capability family to define bookable event types, issue restricted booking links, and attach webhooks to one event type. The reviewed connector exposes 15 fixed operations. Choose an AIP capability from this page instead of constru",
  "canonical": "https://getaip.org/docs/connectors/cal-diy/capabilities/event-types-and-private-links",
  "route": "/docs/connectors/cal-diy/capabilities/event-types-and-private-links",
  "source": "docs/connectors/cal-diy/capabilities/event-types-and-private-links.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/cal-diy/capabilities/event-types-and-private-links.md",
    "txt": "/docs/download/connectors/cal-diy/capabilities/event-types-and-private-links.txt",
    "json": "/docs/download/connectors/cal-diy/capabilities/event-types-and-private-links.json",
    "pdf": "/docs/download/connectors/cal-diy/capabilities/event-types-and-private-links.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Cal.diy event types, private links, and webhooks\ndescription: Use the event-type, private-link, and event-type-webhook capability contracts safely\nkind: capability-reference\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: cal-diy\ncapabilityIds:\n  - cap:cal_diy:event_type.list\n  - cap:cal_diy:event_type.get\n  - cap:cal_diy:event_type.create\n  - cap:cal_diy:event_type.update\n  - cap:cal_diy:event_type.delete\n  - cap:cal_diy:event_type.private_link.list\n  - cap:cal_diy:event_type.private_link.create\n  - cap:cal_diy:event_type.private_link.update\n  - cap:cal_diy:event_type.private_link.delete\n  - cap:cal_diy:event_type.webhook.list\n  - cap:cal_diy:event_type.webhook.get\n  - cap:cal_diy:event_type.webhook.create\n  - cap:cal_diy:event_type.webhook.update\n  - cap:cal_diy:event_type.webhook.delete\n  - cap:cal_diy:event_type.webhook.delete_all\n---\n\n# Cal.diy event types, private links, and webhooks\n\nUse this capability family to define bookable event types, issue restricted\nbooking links, and attach webhooks to one event type. The reviewed connector\nexposes 15 fixed operations. Choose an AIP capability from this page instead of\nconstructing a Cal.diy route or capability suffix dynamically.\n\nEvent-type CRUD and event-type webhooks use provider API version `2024-06-14`.\nPrivate links use `2024-04-15`. The webhook payload version `2021-10-20` is a\nseparate value and does not replace either provider API version.\n\n## Choose an operation\n\n### Event types\n\n| Capability | Provider request | Required input | Risk |\n|---|---|---|---|\n| `cap:cal_diy:event_type.list` | `GET /v2/event-types` | None | Low |\n| `cap:cal_diy:event_type.get` | `GET /v2/event-types/{event_type_id}` | `event_type_id` | Low |\n| `cap:cal_diy:event_type.create` | `POST /v2/event-types` | `title`, `slug`, `lengthInMinutes` | Medium |\n| `cap:cal_diy:event_type.update` | `PATCH /v2/event-types/{event_type_id}` | `event_type_id` | Medium |\n| `cap:cal_diy:event_type.delete` | `DELETE /v2/event-types/{event_type_id}` | `event_type_id` | High |\n\n### Private links\n\n| Capability | Provider request | Required input | Risk |\n|---|---|---|---|\n| `cap:cal_diy:event_type.private_link.list` | `GET /v2/event-types/{event_type_id}/private-links` | `event_type_id` | Low |\n| `cap:cal_diy:event_type.private_link.create` | `POST /v2/event-types/{event_type_id}/private-links` | `event_type_id` | Medium |\n| `cap:cal_diy:event_type.private_link.update` | `PATCH /v2/event-types/{event_type_id}/private-links/{link_id}` | `event_type_id`, `link_id` | Medium |\n| `cap:cal_diy:event_type.private_link.delete` | `DELETE /v2/event-types/{event_type_id}/private-links/{link_id}` | `event_type_id`, `link_id` | High |\n\nA private link is access-controlled data even when its provider URL can be\nshared.\n\n### Event-type webhooks\n\n| Capability | Provider request | Required input | Risk |\n|---|---|---|---|\n| `cap:cal_diy:event_type.webhook.list` | `GET /v2/event-types/{event_type_id}/webhooks` | `event_type_id` | Low |\n| `cap:cal_diy:event_type.webhook.get` | `GET /v2/event-types/{event_type_id}/webhooks/{webhook_id}` | Both ids | Low |\n| `cap:cal_diy:event_type.webhook.create` | `POST /v2/event-types/{event_type_id}/webhooks` | Event id, active flag, URL, triggers, secret ref | Medium |\n| `cap:cal_diy:event_type.webhook.update` | `PATCH /v2/event-types/{event_type_id}/webhooks/{webhook_id}` | Both ids | Medium |\n| `cap:cal_diy:event_type.webhook.delete` | `DELETE /v2/event-types/{event_type_id}/webhooks/{webhook_id}` | Both ids | High |\n| `cap:cal_diy:event_type.webhook.delete_all` | `DELETE /v2/event-types/{event_type_id}/webhooks` | `event_type_id` | High |\n\n`delete_all` removes every webhook scoped to the selected event type; it is not\na filtered delete.\n\n## List and read event types\n\n`event_type.list` accepts an object with any subset of these query fields:\n\n| Field | Constraint |\n|---|---|\n| `username` | Non-empty string |\n| `eventSlug` | Non-empty string |\n| `usernames` | Non-empty array of strings |\n| `orgSlug` | Non-empty string |\n| `orgId` | Integer |\n| `sortCreatedAt` | `asc` or `desc` |\n\nAdditional fields are rejected. With no filters, pass an empty object:\n\n```json\n{}\n```\n\nA filtered request can be as small as:\n\n```json\n{\n  \"username\": \"avery\",\n  \"sortCreatedAt\": \"desc\"\n}\n```\n\n`event_type.get` accepts only an integer provider identifier:\n\n```json\n{\n  \"event_type_id\": 42\n}\n```\n\nThe connector percent-encodes path segments and removes path and query fields\nfrom any provider JSON body. Applications pass one AIP input object; they do\nnot partition the provider request themselves.\n\n## Create or update an event type\n\nCreate requires three fields:\n\n```json\n{\n  \"title\": \"Product consultation\",\n  \"slug\": \"product-consultation\",\n  \"lengthInMinutes\": 30\n}\n```\n\nUpdate requires only the event-type id. Include the fields that should change:\n\n```json\n{\n  \"event_type_id\": 42,\n  \"minimumBookingNotice\": 120,\n  \"hidden\": false\n}\n```\n\nThe connector sends `event_type_id` in the provider path and omits it from the\nJSON body. An otherwise empty update is schema-valid but is not a useful read\nor health probe. Use `event_type.get` to inspect current state.\n\nAdditional top-level properties are rejected for both create and update.\n\n### Core and timing fields\n\n| Field | Constraint |\n|---|---|\n| `title` | Non-empty string; required on create |\n| `slug` | Non-empty string; required on create |\n| `lengthInMinutes` | Integer at least `1`; required on create |\n| `lengthInMinutesOptions` | Non-empty, unique array of integers at least `1` |\n| `description` | String |\n| `slotInterval` | Integer |\n| `minimumBookingNotice` | Integer at least `0` |\n| `beforeEventBuffer` | Integer |\n| `afterEventBuffer` | Integer |\n| `offsetStart` | Integer at least `0` |\n| `scheduleId` | Integer |\n\nThe schema intentionally does not add a non-negative minimum to\n`slotInterval`, `beforeEventBuffer`, or `afterEventBuffer`. Do not infer an\napplication range that the published contract does not declare; Cal.diy may\nstill apply provider-side business rules.\n\n### Limits, recurrence, and confirmation\n\n| Field | Accepted shape |\n|---|---|\n| `bookingLimitsCount` | Disabled, or at least one of `day`, `week`, `month`, `year` with integer value at least `1` |\n| `bookingLimitsDuration` | Disabled, or the same period keys with integer value at least `15` |\n| `bookerActiveBookingsLimit` | Disabled, or `maximumActiveBookings` at least `1`, `offerReschedule`, or both |\n| `bookingWindow` | Disabled; rolling `businessDays` or `calendarDays`; or an explicit `range` |\n| `recurrence` | Disabled, or positive `interval` and `occurrences` with `yearly`, `monthly`, or `weekly` frequency |\n| `confirmationPolicy` | Disabled, `always`, or time-threshold policy |\n| `seats` | Disabled, or an active seat policy |\n\nEvery disabled form is exactly:\n\n```json\n{\n  \"disabled\": true\n}\n```\n\nAn active period-limit form uses at least one period key and may set\n`disabled` to `false`:\n\n```json\n{\n  \"week\": 5,\n  \"disabled\": false\n}\n```\n\nA rolling booking window requires `type` and a non-negative numeric `value`.\nAn explicit range requires a non-empty `value` array whose items are ISO dates\nor date-times.\n\nAn active confirmation policy always requires `type` and\n`blockUnconfirmedBookingsInBooker`. Type `time` also requires\n`noticeThreshold`, with unit `minutes` or `hours` and a positive integer\n`count`. Type `always` permits that threshold but does not require it.\n\nAn active seat policy requires `seatsPerTimeSlot` from `1` through `1000`,\n`showAttendeeInfo`, and `showAvailabilityCount`.\n\nThe schema rejects an active recurrence together with an active\n`bookerActiveBookingsLimit`. Disable one of those features before sending the\nother.\n\n### Booking page and presentation fields\n\n| Field | Constraint |\n|---|---|\n| `bookingFields` | Non-empty array of supported field variants |\n| `disableGuests` | Boolean |\n| `onlyShowFirstAvailableSlot` | Boolean |\n| `bookerLayouts` | Default layout plus a non-empty unique enabled-layout list |\n| `requiresBookerEmailVerification` | Boolean |\n| `lockTimeZoneToggleOnBookingPage` | Boolean |\n| `color` | Six-digit light and dark hex colors |\n| `customName` | String |\n| `successRedirectUrl` | URI string |\n| `hidden` | Boolean |\n| `bookingRequiresAuthentication` | Boolean |\n| `interfaceLanguage` | Supported interface-language enum |\n| `showOptimizedSlots` | Boolean |\n\nLayout values are `month`, `week`, and `column`. The default layout must be one\nof those values; the schema does not separately require it to appear in the\nenabled list.\n\nBuilt-in booking-field variants use `type` values `name`, `splitName`, or\n`email`, or fixed `slug` values `title`, `location`, `notes`, `guests`, and\n`rescheduleReason`. Custom variants use these `type` values:\n\n```text\nphone | address | text | url | number | textarea | select | multiselect\nmultiemail | checkbox | radio | boolean\n```\n\nEvery custom variant requires `type`, a non-empty `slug`, `label`, and\n`required`. `select`, `multiselect`, `checkbox`, and `radio` also require a\nnon-empty `options` array. Each variant rejects fields outside its published\nshape.\n\nThe interface-language enum is:\n\n```text\n\"\" | en | ar | az | bg | bn | ca | cs | da | de | el | es | es-419 | eu\net | fi | fr | he | hu | it | ja | km | ko | nl | no | pl | pt-BR | pt\nro | ru | sk-SK | sr | sv | tr | uk | vi | zh-CN | zh-TW\n```\n\nThe empty string is an explicit enum member; it is not a documentation\nplaceholder.\n\n### Calendar, privacy, and media fields\n\n| Field | Constraint |\n|---|---|\n| `destinationCalendar` | Object with non-empty `integration` and `externalId` |\n| `useDestinationCalendarEmail` | Boolean |\n| `hideCalendarNotes` | Boolean |\n| `hideCalendarEventDetails` | Boolean |\n| `hideOrganizerEmail` | Boolean |\n| `calVideoSettings` | Bounded object of recording, transcription, email, and exit-redirect settings |\n| `disableCancelling` | Object with optional Boolean `disabled` |\n| `disableRescheduling` | Object with optional `disabled` and `minutesBefore` at least `1` |\n| `allowReschedulingPastBookings` | Boolean |\n| `allowReschedulingCancelledBookings` | Boolean |\n| `locations` | Non-empty array of supported location variants |\n\n`calVideoSettings.redirectUrlOnExit` accepts a URI string or null. Its other\nfields are Booleans for organizer or guest recording and transcription,\nautomatic recording, transcription email, and guest or organizer controls.\nAdditional fields are rejected.\n\nLocation variants are:\n\n- `address` with non-empty `address` and Boolean `public`;\n- `link` with URI `link` and Boolean `public`;\n- `phone` with E.164-like `phone` and Boolean `public`;\n- `integration` with one supported integration name;\n- marker-only `attendeeAddress`, `attendeePhone`, or `attendeeDefined`.\n\nThe phone pattern is `+` followed by a nonzero digit and 6 through 14 further\ndigits. Supported integration names are:\n\n```text\ncal-video | google-meet | zoom | whereby-video | whatsapp-video | webex-video\ntelegram-video | tandem | sylaps-video | skype-video | sirius-video\nsignal-video | shimmer-video | salesroom-video | roam-video | riverside-video\nping-video | office365-video | mirotalk-video | jitsi | jelly-video\njelly-conferencing | huddle | facetime-video | element-call-video\neightxeight-video | discord-video | demodesk-video | campfire-video\n```\n\n## Delete an event type\n\n`event_type.delete` accepts only:\n\n```json\n{\n  \"event_type_id\": 42\n}\n```\n\nThis is a high-risk destructive mutation. Before dispatch, the connector reads\nthe same event type. The preflight verifies current provider reachability and\nobject visibility; it does not make deletion reversible or retry-safe.\n\nThe delete capability has no rollback contract. A later create operation would\nproduce new provider state and is not an atomic restoration of the deleted\nevent type.\n\n## Manage private links\n\nPrivate-link list requires only `event_type_id`. Create may also set an expiry,\na usage limit, or both:\n\n```json\n{\n  \"event_type_id\": 42,\n  \"expiresAt\": \"2030-01-01T00:00:00Z\",\n  \"maxUsageCount\": 20\n}\n```\n\n`expiresAt` must have date-time format. `maxUsageCount` is an integer at least\n`1`. Both are optional, so policy should decide whether an unbounded private\nlink is acceptable before approval.\n\nUpdate requires the parent id and a non-empty link id:\n\n```json\n{\n  \"event_type_id\": 42,\n  \"link_id\": \"link-7\",\n  \"maxUsageCount\": 10\n}\n```\n\nDelete accepts the same two identifiers and no update fields. It is the\nhigh-risk revocation operation.\n\nCreate, update, and delete first read the parent event type. There is no\nindividual private-link get capability in this catalogue. Use list to inspect\nthe provider-owned collection, and keep returned links under restricted-data\nhandling.\n\nPrivate-link create names `event_type.private_link.delete` as a best-effort\ncompensation. The compensation requires approval and has a 24-hour contract\nwindow. It is neither automatic nor guaranteed.\n\n## Manage event-type webhooks\n\nList requires `event_type_id` and optionally accepts `skip` at least `0` and\n`take` from `1` through `250`. Get and delete require an additional non-empty\n`webhook_id`.\n\nCreate requires all of these fields:\n\n```json\n{\n  \"event_type_id\": 42,\n  \"active\": true,\n  \"subscriberUrl\": \"https://hooks.example.com/cal/cal-hook-01\",\n  \"triggers\": [\"BOOKING_CREATED\"],\n  \"webhook_secret_ref\": \"cal-hook-01\"\n}\n```\n\nIn this example, the deployment-owned destination prefix would be\n`https://hooks.example.com/cal/`. The final path segment must equal the secret\nreference exactly.\n\nUpdate requires only `event_type_id` and `webhook_id`. It may change `active`,\n`subscriberUrl`, `triggers`, `webhook_secret_ref`, or the fixed `version`.\nSending a new secret reference without the bound subscriber URL is rejected.\nChanging the subscriber URL without a valid secret reference is also rejected.\n\nCreate and update may include `version`, but its only valid value is\n`2021-10-20`. The connector inserts that value when it is omitted.\n\n### Trigger catalogue\n\n`triggers` is a non-empty, unique array selected from these 19 values:\n\n```text\nBOOKING_CREATED | BOOKING_PAYMENT_INITIATED | BOOKING_PAID\nBOOKING_RESCHEDULED | BOOKING_REQUESTED | BOOKING_CANCELLED\nBOOKING_REJECTED | BOOKING_NO_SHOW_UPDATED | FORM_SUBMITTED\nMEETING_ENDED | MEETING_STARTED | RECORDING_READY\nRECORDING_TRANSCRIPTION_GENERATED | OOO_CREATED\nAFTER_HOSTS_CAL_VIDEO_NO_SHOW | AFTER_GUESTS_CAL_VIDEO_NO_SHOW\nFORM_SUBMITTED_NO_EVENT | DELEGATION_CREDENTIAL_ERROR\nWRONG_ASSIGNMENT_REPORT\n```\n\n### Secret and destination boundary\n\n`webhook_secret_ref` is an opaque deployment selector, not secret material. It\nmust contain 1 through 128 URL-segment-safe characters, begin with an ASCII\nletter or digit, and then use only letters, digits, `.`, `_`, `~`, or `-`.\n\nThe connector rejects raw `secret` input and custom `payloadTemplate` input. It\nresolves the opaque reference through the deployment secret provider, injects\nthe raw secret only into the provider request, and removes the reference from\nthat request body. Returned secret-like fields are recursively redacted.\n\nWebhook destinations fail closed. A subscriber URL must:\n\n- use HTTPS and contain a host;\n- contain no username, password, query, or fragment;\n- match a configured prefix by scheme, host, and effective port; and\n- have a path equal to the configured prefix plus `webhook_secret_ref`.\n\nThe schema's generic URI format is therefore not the complete authorization\nrule. A schema-valid URL still fails when it is outside the deployment-owned\nprefix and selector binding.\n\nCreate preflights the parent event type. Update and delete preflight the exact\nwebhook. `delete_all` preflights the parent event type, then removes the whole\nevent-type webhook collection.\n\nWebhook create names `event_type.webhook.delete` as a best-effort,\napproval-required compensation with a 24-hour contract window. It does not\nundo webhook deliveries that have already occurred.\n\n## Shared mutation contract\n\nAll ten mutations require human approval and an external-account-scoped\nidempotency key. Medium-risk creates and updates use the\n`customer_visible_change` reason class. The four deletes use\n`destructive_state_change`.\n\nApproval uses the tenant-policy selector with a 900,000 ms validity window and\nrequires a reason, input snapshot, and policy decision. Approval does not grant\nprovider ownership, bypass the destination policy, or make a stale input valid.\n\nEvery mutation is synchronous, declares retry unsafe, and advertises no\nconnector retry support. Preserve the same key only for the same capability,\nexternal account, and canonical input. If the provider outcome is uncertain,\nreconcile durable and provider evidence instead of sending the mutation again.\n\nAll mutations advertise `dry_run`, `plan`, `commit`, and `reconcile`. None of\nthese 15 operations requires a plan before commit. Dry-run fidelity is limited\nto policy and schema checks; it does not prove that Cal.diy will accept a\nprovider-side change.\n\nEvent-type, private-link, and webhook creates have named best-effort delete\ncompensations. Updates and deletes declare rollback unsupported. A compensation\nis a new governed action, not an automatic transaction rollback.\n\n## Read, data, and output contract\n\nThe five reads are low risk, synchronous, retry-safe, and eligible for\nconnector retry. Their idempotency key is optional and action-scoped.\n\nEvent-type CRUD output is confidential. Private-link and event-type-webhook\noutput is restricted. Every operation in this family is marked as containing\nPII and requiring redaction. Apply the stronger tenant policy when returned\nprovider fields contain URLs, booking questions, identity data, or webhook\nconfiguration.\n\nThe connector returns the provider JSON body as AIP output after validation\nand recursive secret-field redaction. The output requires a non-empty string\n`status`; `data`, `pagination`, and additional provider fields are optional.\nThe connector does not publish a complete provider response-field schema.\n\n## Failures and recovery\n\n| Failure | Relevant cause | Safe response |\n|---|---|---|\n| `connector.cal_diy.invalid_action` | Schema mismatch, raw webhook secret, unsupported version, unbound URL, or destination-policy failure | Correct input or trusted deployment policy; do not weaken the boundary |\n| `connector.cal_diy.policy` | Missing mutation idempotency key or unresolved webhook secret reference | Preserve governed input; correct key or deployment secret mapping |\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 still owned | Read durable state and wait |\n| `connector.cal_diy.outcome_unknown` | Prior provider dispatch cannot be proved | Reconcile; never invent a new retry key |\n| `connector.cal_diy.authentication` | Provider returned `401` or `403` | Verify account binding, credential revision, and provider ownership |\n| `connector.cal_diy.state_conflict` | Provider returned `409` or `412` | Refresh the event type or webhook and review the intended change |\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 a dispatched mutation as uncertain |\n| `connector.cal_diy.state` | Durable state or webhook-secret resolution failed | Restore the deployment-owned dependency before another mutation |\n| `connector.cal_diy.invalid_provider_output` | Successful provider body lacked a valid `status` | Preserve request evidence and reconcile any mutation |\n\nThe common connector error set also covers permanent provider rejection,\noversized responses, invalid credentials, invalid requests, and durable\npre-dispatch or post-dispatch settlement failures. Use the\n[global error reference](../../../reference/errors.md) for gateway and lifecycle errors.\n\n## Verify an integration\n\nFor every operation, verify the discovered capability id, selected external\naccount, completed AIP action id, and non-empty output `status`.\n\nFor event-type writes, also verify the final provider state with\n`event_type.get`. For private-link changes, list the parent collection and\nhandle returned links as restricted data. For webhook changes, verify the\nevent-type webhook record without exposing the secret, then separately test\nsigned ingress under the deployment's webhook-security procedure.\n\nFor every mutation, retain the approval, input snapshot, external-account\nidempotency key, provider request id when present, and durable settlement\nstate. A follow-up read is confirmation evidence; it does not convert an\nuncertain mutation into a safe retry.\n\nThese checks validate integration behavior. 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- [Authentication and tenant routing](../getting-started/authentication-and-tenant-routing.md)\n- [Cal.diy configuration reference](../reference/configuration.md)\n- [Approvals and policy](../../../concepts/approvals-and-policy.md)\n- [Transactions and compensation](../../../concepts/transactions-and-compensation.md)\n",
    "text": "Cal.diy event types, private links, and webhooks\n\nUse this capability family to define bookable event types, issue restricted\nbooking links, and attach webhooks to one event type. The reviewed connector\nexposes 15 fixed operations. Choose an AIP capability from this page instead of\nconstructing a Cal.diy route or capability suffix dynamically.\n\nEvent-type CRUD and event-type webhooks use provider API version 2024-06-14.\nPrivate links use 2024-04-15. The webhook payload version 2021-10-20 is a\nseparate value and does not replace either provider API version.\n\nChoose an operation\n\nEvent types\n\n| Capability | Provider request | Required input | Risk |\n\n| cap:caldiy:eventtype.list | GET /v2/event-types | None | Low |\n| cap:caldiy:eventtype.get | GET /v2/event-types/{eventtypeid} | eventtypeid | Low |\n| cap:caldiy:eventtype.create | POST /v2/event-types | title, slug, lengthInMinutes | Medium |\n| cap:caldiy:eventtype.update | PATCH /v2/event-types/{eventtypeid} | eventtypeid | Medium |\n| cap:caldiy:eventtype.delete | DELETE /v2/event-types/{eventtypeid} | eventtypeid | High |\n\nPrivate links\n\n| Capability | Provider request | Required input | Risk |\n\n| cap:caldiy:eventtype.privatelink.list | GET /v2/event-types/{eventtypeid}/private-links | eventtypeid | Low |\n| cap:caldiy:eventtype.privatelink.create | POST /v2/event-types/{eventtypeid}/private-links | eventtypeid | Medium |\n| cap:caldiy:eventtype.privatelink.update | PATCH /v2/event-types/{eventtypeid}/private-links/{linkid} | eventtypeid, linkid | Medium |\n| cap:caldiy:eventtype.privatelink.delete | DELETE /v2/event-types/{eventtypeid}/private-links/{linkid} | eventtypeid, linkid | High |\n\nA private link is access-controlled data even when its provider URL can be\nshared.\n\nEvent-type webhooks\n\n| Capability | Provider request | Required input | Risk |\n\n| cap:caldiy:eventtype.webhook.list | GET /v2/event-types/{eventtypeid}/webhooks | eventtypeid | Low |\n| cap:caldiy:eventtype.webhook.get | GET /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | Low |\n| cap:caldiy:eventtype.webhook.create | POST /v2/event-types/{eventtypeid}/webhooks | Event id, active flag, URL, triggers, secret ref | Medium |\n| cap:caldiy:eventtype.webhook.update | PATCH /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | Medium |\n| cap:caldiy:eventtype.webhook.delete | DELETE /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | High |\n| cap:caldiy:eventtype.webhook.deleteall | DELETE /v2/event-types/{eventtypeid}/webhooks | eventtypeid | High |\n\ndeleteall removes every webhook scoped to the selected event type; it is not\na filtered delete.\n\nList and read event types\n\neventtype.list accepts an object with any subset of these query fields:\n\n| Field | Constraint |\n\n| username | Non-empty string |\n| eventSlug | Non-empty string |\n| usernames | Non-empty array of strings |\n| orgSlug | Non-empty string |\n| orgId | Integer |\n| sortCreatedAt | asc or desc |\n\nAdditional fields are rejected. With no filters, pass an empty object:\n\n{}\n\nA filtered request can be as small as:\n\n{\n  \"username\": \"avery\",\n  \"sortCreatedAt\": \"desc\"\n}\n\neventtype.get accepts only an integer provider identifier:\n\n{\n  \"eventtypeid\": 42\n}\n\nThe connector percent-encodes path segments and removes path and query fields\nfrom any provider JSON body. Applications pass one AIP input object; they do\nnot partition the provider request themselves.\n\nCreate or update an event type\n\nCreate requires three fields:\n\n{\n  \"title\": \"Product consultation\",\n  \"slug\": \"product-consultation\",\n  \"lengthInMinutes\": 30\n}\n\nUpdate requires only the event-type id. Include the fields that should change:\n\n{\n  \"eventtypeid\": 42,\n  \"minimumBookingNotice\": 120,\n  \"hidden\": false\n}\n\nThe connector sends eventtypeid in the provider path and omits it from the\nJSON body. An otherwise empty update is schema-valid but is not a useful read\nor health probe. Use eventtype.get to inspect current state.\n\nAdditional top-level properties are rejected for both create and update.\n\nCore and timing fields\n\n| Field | Constraint |\n\n| title | Non-empty string; required on create |\n| slug | Non-empty string; required on create |\n| lengthInMinutes | Integer at least 1; required on create |\n| lengthInMinutesOptions | Non-empty, unique array of integers at least 1 |\n| description | String |\n| slotInterval | Integer |\n| minimumBookingNotice | Integer at least 0 |\n| beforeEventBuffer | Integer |\n| afterEventBuffer | Integer |\n| offsetStart | Integer at least 0 |\n| scheduleId | Integer |\n\nThe schema intentionally does not add a non-negative minimum to\nslotInterval, beforeEventBuffer, or afterEventBuffer. Do not infer an\napplication range that the published contract does not declare; Cal.diy may\nstill apply provider-side business rules.\n\nLimits, recurrence, and confirmation\n\n| Field | Accepted shape |\n\n| bookingLimitsCount | Disabled, or at least one of day, week, month, year with integer value at least 1 |\n| bookingLimitsDuration | Disabled, or the same period keys with integer value at least 15 |\n| bookerActiveBookingsLimit | Disabled, or maximumActiveBookings at least 1, offerReschedule, or both |\n| bookingWindow | Disabled; rolling businessDays or calendarDays; or an explicit range |\n| recurrence | Disabled, or positive interval and occurrences with yearly, monthly, or weekly frequency |\n| confirmationPolicy | Disabled, always, or time-threshold policy |\n| seats | Disabled, or an active seat policy |\n\nEvery disabled form is exactly:\n\n{\n  \"disabled\": true\n}\n\nAn active period-limit form uses at least one period key and may set\ndisabled to false:\n\n{\n  \"week\": 5,\n  \"disabled\": false\n}\n\nA rolling booking window requires type and a non-negative numeric value.\nAn explicit range requires a non-empty value array whose items are ISO dates\nor date-times.\n\nAn active confirmation policy always requires type and\nblockUnconfirmedBookingsInBooker. Type time also requires\nnoticeThreshold, with unit minutes or hours and a positive integer\ncount. Type always permits that threshold but does not require it.\n\nAn active seat policy requires seatsPerTimeSlot from 1 through 1000,\nshowAttendeeInfo, and showAvailabilityCount.\n\nThe schema rejects an active recurrence together with an active\nbookerActiveBookingsLimit. Disable one of those features before sending the\nother.\n\nBooking page and presentation fields\n\n| Field | Constraint |\n\n| bookingFields | Non-empty array of supported field variants |\n| disableGuests | Boolean |\n| onlyShowFirstAvailableSlot | Boolean |\n| bookerLayouts | Default layout plus a non-empty unique enabled-layout list |\n| requiresBookerEmailVerification | Boolean |\n| lockTimeZoneToggleOnBookingPage | Boolean |\n| color | Six-digit light and dark hex colors |\n| customName | String |\n| successRedirectUrl | URI string |\n| hidden | Boolean |\n| bookingRequiresAuthentication | Boolean |\n| interfaceLanguage | Supported interface-language enum |\n| showOptimizedSlots | Boolean |\n\nLayout values are month, week, and column. The default layout must be one\nof those values; the schema does not separately require it to appear in the\nenabled list.\n\nBuilt-in booking-field variants use type values name, splitName, or\nemail, or fixed slug values title, location, notes, guests, and\nrescheduleReason. Custom variants use these type values:\n\nphone | address | text | url | number | textarea | select | multiselect\nmultiemail | checkbox | radio | boolean\n\nEvery custom variant requires type, a non-empty slug, label, and\nrequired. select, multiselect, checkbox, and radio also require a\nnon-empty options array. Each variant rejects fields outside its published\nshape.\n\nThe interface-language enum is:\n\n\"\" | en | ar | az | bg | bn | ca | cs | da | de | el | es | es-419 | eu\net | fi | fr | he | hu | it | ja | km | ko | nl | no | pl | pt-BR | pt\nro | ru | sk-SK | sr | sv | tr | uk | vi | zh-CN | zh-TW\n\nThe empty string is an explicit enum member; it is not a documentation\nplaceholder.\n\nCalendar, privacy, and media fields\n\n| Field | Constraint |\n\n| destinationCalendar | Object with non-empty integration and externalId |\n| useDestinationCalendarEmail | Boolean |\n| hideCalendarNotes | Boolean |\n| hideCalendarEventDetails | Boolean |\n| hideOrganizerEmail | Boolean |\n| calVideoSettings | Bounded object of recording, transcription, email, and exit-redirect settings |\n| disableCancelling | Object with optional Boolean disabled |\n| disableRescheduling | Object with optional disabled and minutesBefore at least 1 |\n| allowReschedulingPastBookings | Boolean |\n| allowReschedulingCancelledBookings | Boolean |\n| locations | Non-empty array of supported location variants |\n\ncalVideoSettings.redirectUrlOnExit accepts a URI string or null. Its other\nfields are Booleans for organizer or guest recording and transcription,\nautomatic recording, transcription email, and guest or organizer controls.\nAdditional fields are rejected.\n\nLocation variants are:\n• address with non-empty address and Boolean public;\n• link with URI link and Boolean public;\n• phone with E.164-like phone and Boolean public;\n• integration with one supported integration name;\n• marker-only attendeeAddress, attendeePhone, or attendeeDefined.\n\nThe phone pattern is + followed by a nonzero digit and 6 through 14 further\ndigits. Supported integration names are:\n\ncal-video | google-meet | zoom | whereby-video | whatsapp-video | webex-video\ntelegram-video | tandem | sylaps-video | skype-video | sirius-video\nsignal-video | shimmer-video | salesroom-video | roam-video | riverside-video\nping-video | office365-video | mirotalk-video | jitsi | jelly-video\njelly-conferencing | huddle | facetime-video | element-call-video\neightxeight-video | discord-video | demodesk-video | campfire-video\n\nDelete an event type\n\neventtype.delete accepts only:\n\n{\n  \"eventtypeid\": 42\n}\n\nThis is a high-risk destructive mutation. Before dispatch, the connector reads\nthe same event type. The preflight verifies current provider reachability and\nobject visibility; it does not make deletion reversible or retry-safe.\n\nThe delete capability has no rollback contract. A later create operation would\nproduce new provider state and is not an atomic restoration of the deleted\nevent type.\n\nManage private links\n\nPrivate-link list requires only eventtypeid. Create may also set an expiry,\na usage limit, or both:\n\n{\n  \"eventtypeid\": 42,\n  \"expiresAt\": \"2030-01-01T00:00:00Z\",\n  \"maxUsageCount\": 20\n}\n\nexpiresAt must have date-time format. maxUsageCount is an integer at least\n1. Both are optional, so policy should decide whether an unbounded private\nlink is acceptable before approval.\n\nUpdate requires the parent id and a non-empty link id:\n\n{\n  \"eventtypeid\": 42,\n  \"linkid\": \"link-7\",\n  \"maxUsageCount\": 10\n}\n\nDelete accepts the same two identifiers and no update fields. It is the\nhigh-risk revocation operation.\n\nCreate, update, and delete first read the parent event type. There is no\nindividual private-link get capability in this catalogue. Use list to inspect\nthe provider-owned collection, and keep returned links under restricted-data\nhandling.\n\nPrivate-link create names eventtype.privatelink.delete as a best-effort\ncompensation. The compensation requires approval and has a 24-hour contract\nwindow. It is neither automatic nor guaranteed.\n\nManage event-type webhooks\n\nList requires eventtypeid and optionally accepts skip at least 0 and\ntake from 1 through 250. Get and delete require an additional non-empty\nwebhookid.\n\nCreate requires all of these fields:\n\n{\n  \"eventtypeid\": 42,\n  \"active\": true,\n  \"subscriberUrl\": \"https://hooks.example.com/cal/cal-hook-01\",\n  \"triggers\": [\"BOOKINGCREATED\"],\n  \"webhooksecretref\": \"cal-hook-01\"\n}\n\nIn this example, the deployment-owned destination prefix would be\nhttps://hooks.example.com/cal/. The final path segment must equal the secret\nreference exactly.\n\nUpdate requires only eventtypeid and webhookid. It may change active,\nsubscriberUrl, triggers, webhooksecretref, or the fixed version.\nSending a new secret reference without the bound subscriber URL is rejected.\nChanging the subscriber URL without a valid secret reference is also rejected.\n\nCreate and update may include version, but its only valid value is\n2021-10-20. The connector inserts that value when it is omitted.\n\nTrigger catalogue\n\ntriggers is a non-empty, unique array selected from these 19 values:\n\nBOOKINGCREATED | BOOKINGPAYMENTINITIATED | BOOKINGPAID\nBOOKINGRESCHEDULED | BOOKINGREQUESTED | BOOKINGCANCELLED\nBOOKINGREJECTED | BOOKINGNOSHOWUPDATED | FORMSUBMITTED\nMEETINGENDED | MEETINGSTARTED | RECORDINGREADY\nRECORDINGTRANSCRIPTIONGENERATED | OOOCREATED\nAFTERHOSTSCALVIDEONOSHOW | AFTERGUESTSCALVIDEONOSHOW\nFORMSUBMITTEDNOEVENT | DELEGATIONCREDENTIALERROR\nWRONGASSIGNMENTREPORT\n\nSecret and destination boundary\n\nwebhooksecretref is an opaque deployment selector, not secret material. It\nmust contain 1 through 128 URL-segment-safe characters, begin with an ASCII\nletter or digit, and then use only letters, digits, ., , , or -.\n\nThe connector rejects raw secret input and custom payloadTemplate input. It\nresolves the opaque reference through the deployment secret provider, injects\nthe raw secret only into the provider request, and removes the reference from\nthat request body. Returned secret-like fields are recursively redacted.\n\nWebhook destinations fail closed. A subscriber URL must:\n• use HTTPS and contain a host;\n• contain no username, password, query, or fragment;\n• match a configured prefix by scheme, host, and effective port; and\n• have a path equal to the configured prefix plus webhooksecretref.\n\nThe schema's generic URI format is therefore not the complete authorization\nrule. A schema-valid URL still fails when it is outside the deployment-owned\nprefix and selector binding.\n\nCreate preflights the parent event type. Update and delete preflight the exact\nwebhook. deleteall preflights the parent event type, then removes the whole\nevent-type webhook collection.\n\nWebhook create names eventtype.webhook.delete as a best-effort,\napproval-required compensation with a 24-hour contract window. It does not\nundo webhook deliveries that have already occurred.\n\nShared mutation contract\n\nAll ten mutations require human approval and an external-account-scoped\nidempotency key. Medium-risk creates and updates use the\ncustomervisiblechange reason class. The four deletes use\ndestructivestatechange.\n\nApproval uses the tenant-policy selector with a 900,000 ms validity window and\nrequires a reason, input snapshot, and policy decision. Approval does not grant\nprovider ownership, bypass the destination policy, or make a stale input valid.\n\nEvery mutation is synchronous, declares retry unsafe, and advertises no\nconnector retry support. Preserve the same key only for the same capability,\nexternal account, and canonical input. If the provider outcome is uncertain,\nreconcile durable and provider evidence instead of sending the mutation again.\n\nAll mutations advertise dryrun, plan, commit, and reconcile. None of\nthese 15 operations requires a plan before commit. Dry-run fidelity is limited\nto policy and schema checks; it does not prove that Cal.diy will accept a\nprovider-side change.\n\nEvent-type, private-link, and webhook creates have named best-effort delete\ncompensations. Updates and deletes declare rollback unsupported. A compensation\nis a new governed action, not an automatic transaction rollback.\n\nRead, data, and output contract\n\nThe five reads are low risk, synchronous, retry-safe, and eligible for\nconnector retry. Their idempotency key is optional and action-scoped.\n\nEvent-type CRUD output is confidential. Private-link and event-type-webhook\noutput is restricted. Every operation in this family is marked as containing\nPII and requiring redaction. Apply the stronger tenant policy when returned\nprovider fields contain URLs, booking questions, identity data, or webhook\nconfiguration.\n\nThe connector returns the provider JSON body as AIP output after validation\nand recursive secret-field redaction. The output requires a non-empty string\nstatus; data, pagination, and additional provider fields are optional.\nThe connector does not publish a complete provider response-field schema.\n\nFailures and recovery\n\n| Failure | Relevant cause | Safe response |\n\n| connector.caldiy.invalidaction | Schema mismatch, raw webhook secret, unsupported version, unbound URL, or destination-policy failure | Correct input or trusted deployment policy; do not weaken the boundary |\n| connector.caldiy.policy | Missing mutation idempotency key or unresolved webhook secret reference | Preserve governed input; correct key or deployment secret mapping |\n| connector.caldiy.idempotencycollision | One key owns different canonical input | Stop and inspect the original action |\n| connector.caldiy.inflight | The matching mutation is still owned | Read durable state and wait |\n| connector.caldiy.outcomeunknown | Prior provider dispatch cannot be proved | Reconcile; never invent a new retry key |\n| connector.caldiy.authentication | Provider returned 401 or 403 | Verify account binding, credential revision, and provider ownership |\n| connector.caldiy.stateconflict | Provider returned 409 or 412 | Refresh the event type or webhook and review the intended change |\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 a dispatched mutation as uncertain |\n| connector.caldiy.state | Durable state or webhook-secret resolution failed | Restore the deployment-owned dependency before another mutation |\n| connector.caldiy.invalidprovideroutput | Successful provider body lacked a valid status | Preserve request evidence and reconcile any mutation |\n\nThe common connector error set also covers permanent provider rejection,\noversized responses, invalid credentials, invalid requests, and durable\npre-dispatch or post-dispatch settlement failures. Use the\nglobal error reference (../../../reference/errors.md) for gateway and lifecycle errors.\n\nVerify an integration\n\nFor every operation, verify the discovered capability id, selected external\naccount, completed AIP action id, and non-empty output status.\n\nFor event-type writes, also verify the final provider state with\neventtype.get. For private-link changes, list the parent collection and\nhandle returned links as restricted data. For webhook changes, verify the\nevent-type webhook record without exposing the secret, then separately test\nsigned ingress under the deployment's webhook-security procedure.\n\nFor every mutation, retain the approval, input snapshot, external-account\nidempotency key, provider request id when present, and durable settlement\nstate. A follow-up read is confirmation evidence; it does not convert an\nuncertain mutation into a safe retry.\n\nThese checks validate integration behavior. 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• Authentication and tenant routing (../getting-started/authentication-and-tenant-routing.md)\n• Cal.diy configuration reference (../reference/configuration.md)\n• Approvals and policy (../../../concepts/approvals-and-policy.md)\n• Transactions and compensation (../../../concepts/transactions-and-compensation.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "5f0318ad45d93fe077618483da5c023f334b83080982e81a2142d232d22013be"
  }
}
