{
  "schemaVersion": "1.0",
  "title": "Cal.diy user webhooks",
  "description": "Use these capabilities to list, read, create, update, or delete user-level Cal.diy webhook subscriptions. This page owns five management operations and the exact trigger catalog they accept.",
  "canonical": "https://getaip.org/docs/connectors/cal-diy/capabilities/webhooks",
  "route": "/docs/connectors/cal-diy/capabilities/webhooks",
  "source": "docs/connectors/cal-diy/capabilities/webhooks.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/cal-diy/capabilities/webhooks.md",
    "txt": "/docs/download/connectors/cal-diy/capabilities/webhooks.txt",
    "json": "/docs/download/connectors/cal-diy/capabilities/webhooks.json",
    "pdf": "/docs/download/connectors/cal-diy/capabilities/webhooks.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Cal.diy user webhooks\ndescription: Manage user-level webhook subscriptions without exposing signing secrets\nkind: capability-reference\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: cal-diy\ncapabilityIds:\n  - cap:cal_diy:webhook.list\n  - cap:cal_diy:webhook.get\n  - cap:cal_diy:webhook.create\n  - cap:cal_diy:webhook.update\n  - cap:cal_diy:webhook.delete\n---\n\n# Cal.diy user webhooks\n\nUse these capabilities to list, read, create, update, or delete user-level\nCal.diy webhook subscriptions. This page owns five management operations and\nthe exact trigger catalog they accept.\n\nSubscription management is distinct from inbound delivery. These operations\nconfigure where Cal.diy sends events; they do not verify signatures, enforce a\nreplay fence, or prove that any delivery occurred.\n\nAll five operations use provider API version `2024-08-13`. Managed webhook\npayloads use the separate fixed version `2021-10-20`.\n\n## Choose an operation\n\n| Capability | Provider request | Risk | Approval |\n|---|---|---|---|\n| `cap:cal_diy:webhook.list` | `GET /v2/webhooks` | Low | No |\n| `cap:cal_diy:webhook.get` | `GET /v2/webhooks/{webhook_id}` | Low | No |\n| `cap:cal_diy:webhook.create` | `POST /v2/webhooks` | Medium | Required |\n| `cap:cal_diy:webhook.update` | `PATCH /v2/webhooks/{webhook_id}` | Medium | Required |\n| `cap:cal_diy:webhook.delete` | `DELETE /v2/webhooks/{webhook_id}` | High | Required |\n\n## List or read subscriptions\n\nList accepts optional query pagination:\n\n```json\n{\n  \"skip\": 0,\n  \"take\": 50\n}\n```\n\n`skip` is an integer of at least `0`. `take` is from `1` through `250`.\nEither field may be omitted. Additional fields are rejected.\n\nRead one subscription with a non-empty provider id:\n\n```json\n{\n  \"webhook_id\": \"provider-webhook-uid\"\n}\n```\n\nBoth reads are low risk and approval-free at the capability level. Their\ndescriptions require secrets to be redacted from returned provider data.\n\n## Supported triggers\n\nCreate and trigger-changing updates accept a non-empty array of unique values\nfrom this exact catalog:\n\n```text\nBOOKING_CREATED\nBOOKING_PAYMENT_INITIATED\nBOOKING_PAID\nBOOKING_RESCHEDULED\nBOOKING_REQUESTED\nBOOKING_CANCELLED\nBOOKING_REJECTED\nBOOKING_NO_SHOW_UPDATED\nFORM_SUBMITTED\nMEETING_ENDED\nMEETING_STARTED\nRECORDING_READY\nRECORDING_TRANSCRIPTION_GENERATED\nOOO_CREATED\nAFTER_HOSTS_CAL_VIDEO_NO_SHOW\nAFTER_GUESTS_CAL_VIDEO_NO_SHOW\nFORM_SUBMITTED_NO_EVENT\nDELEGATION_CREDENTIAL_ERROR\nWRONG_ASSIGNMENT_REPORT\n```\n\nTrigger names are case-sensitive. The catalog defines admitted subscription\ninput; it does not promise that a given Cal.diy account will emit every event.\n\n## Create a subscription\n\nCreate requires active state, destination, triggers, and an opaque secret\nreference:\n\n```json\n{\n  \"active\": true,\n  \"subscriberUrl\": \"https://aip.example.com/connectors/cal-diy/webhooks/bookings\",\n  \"triggers\": [\"BOOKING_CREATED\", \"BOOKING_RESCHEDULED\"],\n  \"webhook_secret_ref\": \"bookings\"\n}\n```\n\n| Field | Constraint |\n|---|---|\n| `active` | Required boolean |\n| `subscriberUrl` | Required absolute HTTPS URL after connector checks |\n| `triggers` | Required non-empty unique trigger array |\n| `webhook_secret_ref` | Required opaque deployment secret selector |\n| `version` | Optional input; when present, exactly `2021-10-20` |\n\nThe connector always inserts `version: 2021-10-20`. Custom payload templates\nare not supported; managed subscriptions use the standard Cal.diy payload.\nAdditional input fields, including raw `secret`, are rejected.\n\n### Bind the destination to the secret selector\n\nThe destination policy is deny-by-default. A trusted deployment configures one\nor more credential-free HTTPS prefixes without query or fragment. For a secret\nreference such as `bookings`, the candidate URL path must equal the configured\nprefix path followed by `bookings`, on the same scheme, host, and effective\nport.\n\nThe candidate URL must also contain no username, password, query, or fragment.\nThis binds the public ingress selector to the deployment secret selector rather\nthan accepting an arbitrary callback URL.\n\n`webhook_secret_ref` must be 1 through 128 characters. Its first character must\nbe alphanumeric; remaining characters may be alphanumeric, `.`, `_`, `~`, or\n`-`.\n\n### Keep the raw secret outside the action\n\nThe action carries only `webhook_secret_ref`. Before provider dispatch, the\nconnector resolves that reference through the deployment secret provider,\ninjects the resolved value as provider field `secret`, and removes\n`webhook_secret_ref` from the request body.\n\nThe raw resolved value is never an AIP input or returned application value. The\ncommon output scrubber replaces provider `secret` and token-like field values\nwith `[REDACTED]`.\n\nCreate is medium risk and declares `webhook.delete` as best-effort\ncompensation. Compensation requires separate approval and is not automatic.\n\n## Update a subscription\n\nUpdate requires only `webhook_id`. It may change `active`, `subscriberUrl`,\n`triggers`, `webhook_secret_ref`, or the fixed `version`:\n\n```json\n{\n  \"webhook_id\": \"provider-webhook-uid\",\n  \"subscriberUrl\": \"https://aip.example.com/connectors/cal-diy/webhooks/bookings-v2\",\n  \"webhook_secret_ref\": \"bookings-v2\",\n  \"triggers\": [\"BOOKING_CREATED\", \"BOOKING_CANCELLED\"]\n}\n```\n\nDestination and secret changes are coupled:\n\n- changing `subscriberUrl` requires a valid `webhook_secret_ref`;\n- supplying `webhook_secret_ref` for rotation requires `subscriberUrl`;\n- the destination must bind to that exact reference under the configured\n  prefix policy.\n\nThe connector resolves and injects the new secret with the same create-time\nboundary. It also inserts the fixed webhook version.\n\nAn identifier-only update can pass the published schema without expressing a\nuseful change. Include at least one intentional patch field. The connector\nreads the exact subscription before update, but this preflight does not lock\nprovider state or validate eventual delivery.\n\nUpdate is medium risk and declares no compensating capability.\n\n## Delete a subscription\n\nDelete accepts only the non-empty `webhook_id`. The connector reads that\nsubscription before deletion.\n\nDelete is high risk and destructive, with no rollback contract. Recreating a\nsubscription later is a new provider action and does not restore the prior\nsubscription or its delivery history.\n\n## Shared read contract\n\nThe two reads are synchronous, retry-safe, and eligible for connector retry.\nTheir idempotency key is optional, action-scoped, and collision-checked against\nthe input hash. No capability-level human approval is required.\n\n## Shared mutation contract\n\nAll three mutations require:\n\n| Contract field | Value |\n|---|---|\n| Human approval | Required |\n| Approval selector | Tenant policy |\n| Approval validity window | 900,000 ms |\n| Approval evidence | Reason, input snapshot, policy decision |\n| Idempotency | Required; external-account scope |\n| Collision behavior | Revalidate input hash |\n| Connector retry | Not supported |\n| Retry safety | Unsafe |\n| Execution | Synchronous only |\n| Transaction modes | `dry_run`, `plan`, `commit`, `reconcile` |\n| Plan before commit | Not required |\n| Dry-run fidelity | Policy and schema only |\n\nCreate and update are medium risk. Delete is high risk, destructive, and uses\nthe destructive approval reason. None carries the `send_message` side-effect\nmarker.\n\nOnly create declares best-effort delete compensation. Update and delete declare\nrollback as not supported.\n\n## Data and result contract\n\nAll five operations are restricted, contain PII, and require redaction. URLs,\ntrigger configuration, subscription identifiers, and provider metadata must\nremain within the authorized tenant and application boundary.\n\nThe connector returns the validated provider JSON body. A non-empty string\n`status` is required. `data`, `pagination`, and additional provider fields are\noptional and provider-owned. Secret-, key-, and token-named values are scrubbed\nrecursively; ordinary PII is not removed by that targeted redactor.\n\n## Failures and recovery\n\n| Failure | Relevant cause | Safe response |\n|---|---|---|\n| `connector.cal_diy.invalid_action` | Invalid trigger, URL, selector binding, rotation pair, version, or unknown field | Correct input before dispatch |\n| `connector.cal_diy.policy` | Missing mutation key or unresolved secret reference | Preserve approved input; fix the key or deployment secret |\n| `connector.cal_diy.state` | Secret resolver or durable state failed | Repair the deployment dependency before retry |\n| `connector.cal_diy.idempotency_collision` | One key owns different mutation input | Stop and inspect the original action |\n| `connector.cal_diy.in_flight` | A matching webhook mutation is executing | Read durable state and wait |\n| `connector.cal_diy.outcome_unknown` | Prior provider dispatch cannot be proved | Reconcile; do not create a replacement key |\n| `connector.cal_diy.authentication` | Provider returned `401` or `403` | Verify account binding and provider credentials |\n| `connector.cal_diy.state_conflict` | Provider returned `409` or `412` | Refresh subscription state and review the mutation |\n| `connector.cal_diy.rate_limited` | Provider returned `429` | 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` | Successful response lacked valid `status` | Preserve evidence and reconcile a mutation |\n\nOther permanent provider rejections, temporary provider errors, oversized\nresponses, and durable settlement failures use the common Cal.diy error family.\nGateway and lifecycle errors are described in the\n[global error reference](../../../reference/errors.md).\n\n## Verify subscription management\n\nFor a read, retain the capability, pagination or webhook id, actor, tenant,\nexternal account, response status, and provider request id when present.\n\nFor a mutation, also retain the exact destination, secret reference name,\ntrigger set, approval evidence, and original idempotency key. Never retain the\nresolved raw secret. Wait for a completed durable result, then read the exact\nsubscription.\n\nFor uncertain dispatch, reconcile the original operation before another\nmutation. A completed create or update confirms provider acceptance of the\nmanagement request, not successful signed delivery to the application.\n\n## Related documentation\n\n- [Cal.diy capability index](README.md)\n- [Event-type webhooks](event-types-and-private-links.md)\n- [Authentication and tenant routing](../getting-started/authentication-and-tenant-routing.md)\n- [Cal.diy configuration reference](../reference/configuration.md)\n- [Webhook security and replay](../reference/webhook-security-and-replay.md)\n",
    "text": "Cal.diy user webhooks\n\nUse these capabilities to list, read, create, update, or delete user-level\nCal.diy webhook subscriptions. This page owns five management operations and\nthe exact trigger catalog they accept.\n\nSubscription management is distinct from inbound delivery. These operations\nconfigure where Cal.diy sends events; they do not verify signatures, enforce a\nreplay fence, or prove that any delivery occurred.\n\nAll five operations use provider API version 2024-08-13. Managed webhook\npayloads use the separate fixed version 2021-10-20.\n\nChoose an operation\n\n| Capability | Provider request | Risk | Approval |\n\n| cap:caldiy:webhook.list | GET /v2/webhooks | Low | No |\n| cap:caldiy:webhook.get | GET /v2/webhooks/{webhookid} | Low | No |\n| cap:caldiy:webhook.create | POST /v2/webhooks | Medium | Required |\n| cap:caldiy:webhook.update | PATCH /v2/webhooks/{webhookid} | Medium | Required |\n| cap:caldiy:webhook.delete | DELETE /v2/webhooks/{webhookid} | High | Required |\n\nList or read subscriptions\n\nList accepts optional query pagination:\n\n{\n  \"skip\": 0,\n  \"take\": 50\n}\n\nskip is an integer of at least 0. take is from 1 through 250.\nEither field may be omitted. Additional fields are rejected.\n\nRead one subscription with a non-empty provider id:\n\n{\n  \"webhookid\": \"provider-webhook-uid\"\n}\n\nBoth reads are low risk and approval-free at the capability level. Their\ndescriptions require secrets to be redacted from returned provider data.\n\nSupported triggers\n\nCreate and trigger-changing updates accept a non-empty array of unique values\nfrom this exact catalog:\n\nBOOKINGCREATED\nBOOKINGPAYMENTINITIATED\nBOOKINGPAID\nBOOKINGRESCHEDULED\nBOOKINGREQUESTED\nBOOKINGCANCELLED\nBOOKINGREJECTED\nBOOKINGNOSHOWUPDATED\nFORMSUBMITTED\nMEETINGENDED\nMEETINGSTARTED\nRECORDINGREADY\nRECORDINGTRANSCRIPTIONGENERATED\nOOOCREATED\nAFTERHOSTSCALVIDEONOSHOW\nAFTERGUESTSCALVIDEONOSHOW\nFORMSUBMITTEDNOEVENT\nDELEGATIONCREDENTIALERROR\nWRONGASSIGNMENTREPORT\n\nTrigger names are case-sensitive. The catalog defines admitted subscription\ninput; it does not promise that a given Cal.diy account will emit every event.\n\nCreate a subscription\n\nCreate requires active state, destination, triggers, and an opaque secret\nreference:\n\n{\n  \"active\": true,\n  \"subscriberUrl\": \"https://aip.example.com/connectors/cal-diy/webhooks/bookings\",\n  \"triggers\": [\"BOOKINGCREATED\", \"BOOKINGRESCHEDULED\"],\n  \"webhooksecretref\": \"bookings\"\n}\n\n| Field | Constraint |\n\n| active | Required boolean |\n| subscriberUrl | Required absolute HTTPS URL after connector checks |\n| triggers | Required non-empty unique trigger array |\n| webhooksecretref | Required opaque deployment secret selector |\n| version | Optional input; when present, exactly 2021-10-20 |\n\nThe connector always inserts version: 2021-10-20. Custom payload templates\nare not supported; managed subscriptions use the standard Cal.diy payload.\nAdditional input fields, including raw secret, are rejected.\n\nBind the destination to the secret selector\n\nThe destination policy is deny-by-default. A trusted deployment configures one\nor more credential-free HTTPS prefixes without query or fragment. For a secret\nreference such as bookings, the candidate URL path must equal the configured\nprefix path followed by bookings, on the same scheme, host, and effective\nport.\n\nThe candidate URL must also contain no username, password, query, or fragment.\nThis binds the public ingress selector to the deployment secret selector rather\nthan accepting an arbitrary callback URL.\n\nwebhooksecretref must be 1 through 128 characters. Its first character must\nbe alphanumeric; remaining characters may be alphanumeric, ., , , or\n-.\n\nKeep the raw secret outside the action\n\nThe action carries only webhooksecretref. Before provider dispatch, the\nconnector resolves that reference through the deployment secret provider,\ninjects the resolved value as provider field secret, and removes\nwebhooksecretref from the request body.\n\nThe raw resolved value is never an AIP input or returned application value. The\ncommon output scrubber replaces provider secret and token-like field values\nwith [REDACTED].\n\nCreate is medium risk and declares webhook.delete as best-effort\ncompensation. Compensation requires separate approval and is not automatic.\n\nUpdate a subscription\n\nUpdate requires only webhookid. It may change active, subscriberUrl,\ntriggers, webhooksecretref, or the fixed version:\n\n{\n  \"webhookid\": \"provider-webhook-uid\",\n  \"subscriberUrl\": \"https://aip.example.com/connectors/cal-diy/webhooks/bookings-v2\",\n  \"webhooksecretref\": \"bookings-v2\",\n  \"triggers\": [\"BOOKINGCREATED\", \"BOOKINGCANCELLED\"]\n}\n\nDestination and secret changes are coupled:\n• changing subscriberUrl requires a valid webhooksecretref;\n• supplying webhooksecretref for rotation requires subscriberUrl;\n• the destination must bind to that exact reference under the configured\n  prefix policy.\n\nThe connector resolves and injects the new secret with the same create-time\nboundary. It also inserts the fixed webhook version.\n\nAn identifier-only update can pass the published schema without expressing a\nuseful change. Include at least one intentional patch field. The connector\nreads the exact subscription before update, but this preflight does not lock\nprovider state or validate eventual delivery.\n\nUpdate is medium risk and declares no compensating capability.\n\nDelete a subscription\n\nDelete accepts only the non-empty webhookid. The connector reads that\nsubscription before deletion.\n\nDelete is high risk and destructive, with no rollback contract. Recreating a\nsubscription later is a new provider action and does not restore the prior\nsubscription or its delivery history.\n\nShared read contract\n\nThe two reads are synchronous, retry-safe, and eligible for connector retry.\nTheir idempotency key is optional, action-scoped, and collision-checked against\nthe input hash. No capability-level human approval is required.\n\nShared mutation contract\n\nAll three mutations require:\n\n| Contract field | Value |\n\n| Human approval | Required |\n| Approval selector | Tenant policy |\n| Approval validity window | 900,000 ms |\n| Approval evidence | Reason, input snapshot, policy decision |\n| Idempotency | Required; external-account scope |\n| Collision behavior | Revalidate input hash |\n| Connector retry | Not supported |\n| Retry safety | Unsafe |\n| Execution | Synchronous only |\n| Transaction modes | dryrun, plan, commit, reconcile |\n| Plan before commit | Not required |\n| Dry-run fidelity | Policy and schema only |\n\nCreate and update are medium risk. Delete is high risk, destructive, and uses\nthe destructive approval reason. None carries the sendmessage side-effect\nmarker.\n\nOnly create declares best-effort delete compensation. Update and delete declare\nrollback as not supported.\n\nData and result contract\n\nAll five operations are restricted, contain PII, and require redaction. URLs,\ntrigger configuration, subscription identifiers, and provider metadata must\nremain within the authorized tenant and application boundary.\n\nThe connector returns the validated provider JSON body. A non-empty string\nstatus is required. data, pagination, and additional provider fields are\noptional and provider-owned. Secret-, key-, and token-named values are scrubbed\nrecursively; ordinary PII is not removed by that targeted redactor.\n\nFailures and recovery\n\n| Failure | Relevant cause | Safe response |\n\n| connector.caldiy.invalidaction | Invalid trigger, URL, selector binding, rotation pair, version, or unknown field | Correct input before dispatch |\n| connector.caldiy.policy | Missing mutation key or unresolved secret reference | Preserve approved input; fix the key or deployment secret |\n| connector.caldiy.state | Secret resolver or durable state failed | Repair the deployment dependency before retry |\n| connector.caldiy.idempotencycollision | One key owns different mutation input | Stop and inspect the original action |\n| connector.caldiy.inflight | A matching webhook mutation is executing | Read durable state and wait |\n| connector.caldiy.outcomeunknown | Prior provider dispatch cannot be proved | Reconcile; do not create a replacement key |\n| connector.caldiy.authentication | Provider returned 401 or 403 | Verify account binding and provider credentials |\n| connector.caldiy.stateconflict | Provider returned 409 or 412 | Refresh subscription state and review the mutation |\n| connector.caldiy.ratelimited | Provider returned 429 | 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 | Successful response lacked valid status | Preserve evidence and reconcile a mutation |\n\nOther permanent provider rejections, temporary provider errors, oversized\nresponses, and durable settlement failures use the common Cal.diy error family.\nGateway and lifecycle errors are described in the\nglobal error reference (../../../reference/errors.md).\n\nVerify subscription management\n\nFor a read, retain the capability, pagination or webhook id, actor, tenant,\nexternal account, response status, and provider request id when present.\n\nFor a mutation, also retain the exact destination, secret reference name,\ntrigger set, approval evidence, and original idempotency key. Never retain the\nresolved raw secret. Wait for a completed durable result, then read the exact\nsubscription.\n\nFor uncertain dispatch, reconcile the original operation before another\nmutation. A completed create or update confirms provider acceptance of the\nmanagement request, not successful signed delivery to the application.\n\nRelated documentation\n• Cal.diy capability index (README.md)\n• Event-type webhooks (event-types-and-private-links.md)\n• Authentication and tenant routing (../getting-started/authentication-and-tenant-routing.md)\n• Cal.diy configuration reference (../reference/configuration.md)\n• Webhook security and replay (../reference/webhook-security-and-replay.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "95e818d6863a7c7f95a75da96f18a9d18df57e72a6182dea73baa51aa9a5376d"
  }
}
