Cal.diy event types, private links, and webhooks 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 constructing a Cal.diy route or capability suffix dynamically. Event-type CRUD and event-type webhooks use provider API version 2024-06-14. Private links use 2024-04-15. The webhook payload version 2021-10-20 is a separate value and does not replace either provider API version. Choose an operation Event types | Capability | Provider request | Required input | Risk | | cap:caldiy:eventtype.list | GET /v2/event-types | None | Low | | cap:caldiy:eventtype.get | GET /v2/event-types/{eventtypeid} | eventtypeid | Low | | cap:caldiy:eventtype.create | POST /v2/event-types | title, slug, lengthInMinutes | Medium | | cap:caldiy:eventtype.update | PATCH /v2/event-types/{eventtypeid} | eventtypeid | Medium | | cap:caldiy:eventtype.delete | DELETE /v2/event-types/{eventtypeid} | eventtypeid | High | Private links | Capability | Provider request | Required input | Risk | | cap:caldiy:eventtype.privatelink.list | GET /v2/event-types/{eventtypeid}/private-links | eventtypeid | Low | | cap:caldiy:eventtype.privatelink.create | POST /v2/event-types/{eventtypeid}/private-links | eventtypeid | Medium | | cap:caldiy:eventtype.privatelink.update | PATCH /v2/event-types/{eventtypeid}/private-links/{linkid} | eventtypeid, linkid | Medium | | cap:caldiy:eventtype.privatelink.delete | DELETE /v2/event-types/{eventtypeid}/private-links/{linkid} | eventtypeid, linkid | High | A private link is access-controlled data even when its provider URL can be shared. Event-type webhooks | Capability | Provider request | Required input | Risk | | cap:caldiy:eventtype.webhook.list | GET /v2/event-types/{eventtypeid}/webhooks | eventtypeid | Low | | cap:caldiy:eventtype.webhook.get | GET /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | Low | | cap:caldiy:eventtype.webhook.create | POST /v2/event-types/{eventtypeid}/webhooks | Event id, active flag, URL, triggers, secret ref | Medium | | cap:caldiy:eventtype.webhook.update | PATCH /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | Medium | | cap:caldiy:eventtype.webhook.delete | DELETE /v2/event-types/{eventtypeid}/webhooks/{webhookid} | Both ids | High | | cap:caldiy:eventtype.webhook.deleteall | DELETE /v2/event-types/{eventtypeid}/webhooks | eventtypeid | High | deleteall removes every webhook scoped to the selected event type; it is not a filtered delete. List and read event types eventtype.list accepts an object with any subset of these query fields: | Field | Constraint | | username | Non-empty string | | eventSlug | Non-empty string | | usernames | Non-empty array of strings | | orgSlug | Non-empty string | | orgId | Integer | | sortCreatedAt | asc or desc | Additional fields are rejected. With no filters, pass an empty object: {} A filtered request can be as small as: { "username": "avery", "sortCreatedAt": "desc" } eventtype.get accepts only an integer provider identifier: { "eventtypeid": 42 } The connector percent-encodes path segments and removes path and query fields from any provider JSON body. Applications pass one AIP input object; they do not partition the provider request themselves. Create or update an event type Create requires three fields: { "title": "Product consultation", "slug": "product-consultation", "lengthInMinutes": 30 } Update requires only the event-type id. Include the fields that should change: { "eventtypeid": 42, "minimumBookingNotice": 120, "hidden": false } The connector sends eventtypeid in the provider path and omits it from the JSON body. An otherwise empty update is schema-valid but is not a useful read or health probe. Use eventtype.get to inspect current state. Additional top-level properties are rejected for both create and update. Core and timing fields | Field | Constraint | | title | Non-empty string; required on create | | slug | Non-empty string; required on create | | lengthInMinutes | Integer at least 1; required on create | | lengthInMinutesOptions | Non-empty, unique array of integers at least 1 | | description | String | | slotInterval | Integer | | minimumBookingNotice | Integer at least 0 | | beforeEventBuffer | Integer | | afterEventBuffer | Integer | | offsetStart | Integer at least 0 | | scheduleId | Integer | The schema intentionally does not add a non-negative minimum to slotInterval, beforeEventBuffer, or afterEventBuffer. Do not infer an application range that the published contract does not declare; Cal.diy may still apply provider-side business rules. Limits, recurrence, and confirmation | Field | Accepted shape | | bookingLimitsCount | Disabled, or at least one of day, week, month, year with integer value at least 1 | | bookingLimitsDuration | Disabled, or the same period keys with integer value at least 15 | | bookerActiveBookingsLimit | Disabled, or maximumActiveBookings at least 1, offerReschedule, or both | | bookingWindow | Disabled; rolling businessDays or calendarDays; or an explicit range | | recurrence | Disabled, or positive interval and occurrences with yearly, monthly, or weekly frequency | | confirmationPolicy | Disabled, always, or time-threshold policy | | seats | Disabled, or an active seat policy | Every disabled form is exactly: { "disabled": true } An active period-limit form uses at least one period key and may set disabled to false: { "week": 5, "disabled": false } A rolling booking window requires type and a non-negative numeric value. An explicit range requires a non-empty value array whose items are ISO dates or date-times. An active confirmation policy always requires type and blockUnconfirmedBookingsInBooker. Type time also requires noticeThreshold, with unit minutes or hours and a positive integer count. Type always permits that threshold but does not require it. An active seat policy requires seatsPerTimeSlot from 1 through 1000, showAttendeeInfo, and showAvailabilityCount. The schema rejects an active recurrence together with an active bookerActiveBookingsLimit. Disable one of those features before sending the other. Booking page and presentation fields | Field | Constraint | | bookingFields | Non-empty array of supported field variants | | disableGuests | Boolean | | onlyShowFirstAvailableSlot | Boolean | | bookerLayouts | Default layout plus a non-empty unique enabled-layout list | | requiresBookerEmailVerification | Boolean | | lockTimeZoneToggleOnBookingPage | Boolean | | color | Six-digit light and dark hex colors | | customName | String | | successRedirectUrl | URI string | | hidden | Boolean | | bookingRequiresAuthentication | Boolean | | interfaceLanguage | Supported interface-language enum | | showOptimizedSlots | Boolean | Layout values are month, week, and column. The default layout must be one of those values; the schema does not separately require it to appear in the enabled list. Built-in booking-field variants use type values name, splitName, or email, or fixed slug values title, location, notes, guests, and rescheduleReason. Custom variants use these type values: phone | address | text | url | number | textarea | select | multiselect multiemail | checkbox | radio | boolean Every custom variant requires type, a non-empty slug, label, and required. select, multiselect, checkbox, and radio also require a non-empty options array. Each variant rejects fields outside its published shape. The interface-language enum is: "" | en | ar | az | bg | bn | ca | cs | da | de | el | es | es-419 | eu et | fi | fr | he | hu | it | ja | km | ko | nl | no | pl | pt-BR | pt ro | ru | sk-SK | sr | sv | tr | uk | vi | zh-CN | zh-TW The empty string is an explicit enum member; it is not a documentation placeholder. Calendar, privacy, and media fields | Field | Constraint | | destinationCalendar | Object with non-empty integration and externalId | | useDestinationCalendarEmail | Boolean | | hideCalendarNotes | Boolean | | hideCalendarEventDetails | Boolean | | hideOrganizerEmail | Boolean | | calVideoSettings | Bounded object of recording, transcription, email, and exit-redirect settings | | disableCancelling | Object with optional Boolean disabled | | disableRescheduling | Object with optional disabled and minutesBefore at least 1 | | allowReschedulingPastBookings | Boolean | | allowReschedulingCancelledBookings | Boolean | | locations | Non-empty array of supported location variants | calVideoSettings.redirectUrlOnExit accepts a URI string or null. Its other fields are Booleans for organizer or guest recording and transcription, automatic recording, transcription email, and guest or organizer controls. Additional fields are rejected. Location variants are: • address with non-empty address and Boolean public; • link with URI link and Boolean public; • phone with E.164-like phone and Boolean public; • integration with one supported integration name; • marker-only attendeeAddress, attendeePhone, or attendeeDefined. The phone pattern is + followed by a nonzero digit and 6 through 14 further digits. Supported integration names are: cal-video | google-meet | zoom | whereby-video | whatsapp-video | webex-video telegram-video | tandem | sylaps-video | skype-video | sirius-video signal-video | shimmer-video | salesroom-video | roam-video | riverside-video ping-video | office365-video | mirotalk-video | jitsi | jelly-video jelly-conferencing | huddle | facetime-video | element-call-video eightxeight-video | discord-video | demodesk-video | campfire-video Delete an event type eventtype.delete accepts only: { "eventtypeid": 42 } This is a high-risk destructive mutation. Before dispatch, the connector reads the same event type. The preflight verifies current provider reachability and object visibility; it does not make deletion reversible or retry-safe. The delete capability has no rollback contract. A later create operation would produce new provider state and is not an atomic restoration of the deleted event type. Manage private links Private-link list requires only eventtypeid. Create may also set an expiry, a usage limit, or both: { "eventtypeid": 42, "expiresAt": "2030-01-01T00:00:00Z", "maxUsageCount": 20 } expiresAt must have date-time format. maxUsageCount is an integer at least 1. Both are optional, so policy should decide whether an unbounded private link is acceptable before approval. Update requires the parent id and a non-empty link id: { "eventtypeid": 42, "linkid": "link-7", "maxUsageCount": 10 } Delete accepts the same two identifiers and no update fields. It is the high-risk revocation operation. Create, update, and delete first read the parent event type. There is no individual private-link get capability in this catalogue. Use list to inspect the provider-owned collection, and keep returned links under restricted-data handling. Private-link create names eventtype.privatelink.delete as a best-effort compensation. The compensation requires approval and has a 24-hour contract window. It is neither automatic nor guaranteed. Manage event-type webhooks List requires eventtypeid and optionally accepts skip at least 0 and take from 1 through 250. Get and delete require an additional non-empty webhookid. Create requires all of these fields: { "eventtypeid": 42, "active": true, "subscriberUrl": "https://hooks.example.com/cal/cal-hook-01", "triggers": ["BOOKINGCREATED"], "webhooksecretref": "cal-hook-01" } In this example, the deployment-owned destination prefix would be https://hooks.example.com/cal/. The final path segment must equal the secret reference exactly. Update requires only eventtypeid and webhookid. It may change active, subscriberUrl, triggers, webhooksecretref, or the fixed version. Sending a new secret reference without the bound subscriber URL is rejected. Changing the subscriber URL without a valid secret reference is also rejected. Create and update may include version, but its only valid value is 2021-10-20. The connector inserts that value when it is omitted. Trigger catalogue triggers is a non-empty, unique array selected from these 19 values: BOOKINGCREATED | BOOKINGPAYMENTINITIATED | BOOKINGPAID BOOKINGRESCHEDULED | BOOKINGREQUESTED | BOOKINGCANCELLED BOOKINGREJECTED | BOOKINGNOSHOWUPDATED | FORMSUBMITTED MEETINGENDED | MEETINGSTARTED | RECORDINGREADY RECORDINGTRANSCRIPTIONGENERATED | OOOCREATED AFTERHOSTSCALVIDEONOSHOW | AFTERGUESTSCALVIDEONOSHOW FORMSUBMITTEDNOEVENT | DELEGATIONCREDENTIALERROR WRONGASSIGNMENTREPORT Secret and destination boundary webhooksecretref is an opaque deployment selector, not secret material. It must contain 1 through 128 URL-segment-safe characters, begin with an ASCII letter or digit, and then use only letters, digits, ., , , or -. The connector rejects raw secret input and custom payloadTemplate input. It resolves the opaque reference through the deployment secret provider, injects the raw secret only into the provider request, and removes the reference from that request body. Returned secret-like fields are recursively redacted. Webhook destinations fail closed. A subscriber URL must: • use HTTPS and contain a host; • contain no username, password, query, or fragment; • match a configured prefix by scheme, host, and effective port; and • have a path equal to the configured prefix plus webhooksecretref. The schema's generic URI format is therefore not the complete authorization rule. A schema-valid URL still fails when it is outside the deployment-owned prefix and selector binding. Create preflights the parent event type. Update and delete preflight the exact webhook. deleteall preflights the parent event type, then removes the whole event-type webhook collection. Webhook create names eventtype.webhook.delete as a best-effort, approval-required compensation with a 24-hour contract window. It does not undo webhook deliveries that have already occurred. Shared mutation contract All ten mutations require human approval and an external-account-scoped idempotency key. Medium-risk creates and updates use the customervisiblechange reason class. The four deletes use destructivestatechange. Approval uses the tenant-policy selector with a 900,000 ms validity window and requires a reason, input snapshot, and policy decision. Approval does not grant provider ownership, bypass the destination policy, or make a stale input valid. Every mutation is synchronous, declares retry unsafe, and advertises no connector retry support. Preserve the same key only for the same capability, external account, and canonical input. If the provider outcome is uncertain, reconcile durable and provider evidence instead of sending the mutation again. All mutations advertise dryrun, plan, commit, and reconcile. None of these 15 operations requires a plan before commit. Dry-run fidelity is limited to policy and schema checks; it does not prove that Cal.diy will accept a provider-side change. Event-type, private-link, and webhook creates have named best-effort delete compensations. Updates and deletes declare rollback unsupported. A compensation is a new governed action, not an automatic transaction rollback. Read, data, and output contract The five reads are low risk, synchronous, retry-safe, and eligible for connector retry. Their idempotency key is optional and action-scoped. Event-type CRUD output is confidential. Private-link and event-type-webhook output is restricted. Every operation in this family is marked as containing PII and requiring redaction. Apply the stronger tenant policy when returned provider fields contain URLs, booking questions, identity data, or webhook configuration. The connector returns the provider JSON body as AIP output after validation and recursive secret-field redaction. The output requires a non-empty string status; data, pagination, and additional provider fields are optional. The connector does not publish a complete provider response-field schema. Failures and recovery | Failure | Relevant cause | Safe response | | 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 | | connector.caldiy.policy | Missing mutation idempotency key or unresolved webhook secret reference | Preserve governed input; correct key or deployment secret mapping | | connector.caldiy.idempotencycollision | One key owns different canonical input | Stop and inspect the original action | | connector.caldiy.inflight | The matching mutation is still owned | Read durable state and wait | | connector.caldiy.outcomeunknown | Prior provider dispatch cannot be proved | Reconcile; never invent a new retry key | | connector.caldiy.authentication | Provider returned 401 or 403 | Verify account binding, credential revision, and provider ownership | | connector.caldiy.stateconflict | Provider returned 409 or 412 | Refresh the event type or webhook and review the intended change | | connector.caldiy.ratelimited | Provider returned 429 | Retry reads within policy; reconcile mutations | | connector.caldiy.remotetemporary | Provider returned a server error | Retry reads within policy; reconcile mutations | | connector.caldiy.transport | Provider exchange failed | Retry reads only; treat a dispatched mutation as uncertain | | connector.caldiy.state | Durable state or webhook-secret resolution failed | Restore the deployment-owned dependency before another mutation | | connector.caldiy.invalidprovideroutput | Successful provider body lacked a valid status | Preserve request evidence and reconcile any mutation | The common connector error set also covers permanent provider rejection, oversized responses, invalid credentials, invalid requests, and durable pre-dispatch or post-dispatch settlement failures. Use the global error reference (../../../reference/errors.md) for gateway and lifecycle errors. Verify an integration For every operation, verify the discovered capability id, selected external account, completed AIP action id, and non-empty output status. For event-type writes, also verify the final provider state with eventtype.get. For private-link changes, list the parent collection and handle returned links as restricted data. For webhook changes, verify the event-type webhook record without exposing the secret, then separately test signed ingress under the deployment's webhook-security procedure. For every mutation, retain the approval, input snapshot, external-account idempotency key, provider request id when present, and durable settlement state. A follow-up read is confirmation evidence; it does not convert an uncertain mutation into a safe retry. These checks validate integration behavior. They do not establish live Cal.diy qualification or production readiness for a specific deployment. Related documentation • Cal.diy capability index (README.md) • Authentication and tenant routing (../getting-started/authentication-and-tenant-routing.md) • Cal.diy configuration reference (../reference/configuration.md) • Approvals and policy (../../../concepts/approvals-and-policy.md) • Transactions and compensation (../../../concepts/transactions-and-compensation.md)