Files
cal-diy-oidc/agents/skills/calcom-api/references/webhooks.md
T
Benny Joo ab21c7f805 refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com

This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:

- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions

3893 files changed, 20789 insertions(+), 411020 deletions(-)

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* rip out org related comments in api v2

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-04-15 09:52:36 -03:00

7.3 KiB

Webhooks API Reference

Detailed documentation for webhook management endpoints in the Cal.diy API v2.

Endpoints Overview

Method Endpoint Description
GET /v2/webhooks List webhooks
POST /v2/webhooks Create a webhook
GET /v2/webhooks/{webhookId} Get a webhook
PATCH /v2/webhooks/{webhookId} Update a webhook
DELETE /v2/webhooks/{webhookId} Delete a webhook

List Webhooks

GET /v2/webhooks

Response

{
  "status": "success",
  "data": [
    {
      "id": "webhook-id-123",
      "subscriberUrl": "https://your-app.com/webhook",
      "triggers": ["BOOKING_CREATED", "BOOKING_CANCELLED"],
      "active": true,
      "payloadTemplate": null,
      "secret": "whsec_..."
    }
  ]
}

Create a Webhook

POST /v2/webhooks

Request Body

{
  "subscriberUrl": "https://your-app.com/webhook",
  "triggers": ["BOOKING_CREATED", "BOOKING_CANCELLED", "BOOKING_RESCHEDULED"],
  "active": true,
  "payloadTemplate": null,
  "secret": "your-webhook-secret"
}

Fields

Field Type Required Description
subscriberUrl string Yes URL to receive webhook payloads
triggers array Yes Events that trigger the webhook
active boolean No Enable/disable webhook (default: true)
payloadTemplate string No Custom payload template
secret string No Secret for signature verification

Webhook Triggers

Trigger Description
BOOKING_CREATED New booking created
BOOKING_CANCELLED Booking cancelled
BOOKING_RESCHEDULED Booking rescheduled
BOOKING_CONFIRMED Pending booking confirmed
BOOKING_REJECTED Booking rejected
BOOKING_REQUESTED Booking request received (requires confirmation)
BOOKING_PAYMENT_INITIATED Payment started
BOOKING_NO_SHOW_UPDATED Attendee marked as no-show
MEETING_STARTED Video meeting started
MEETING_ENDED Video meeting ended
RECORDING_READY Meeting recording available
INSTANT_MEETING Instant meeting created
RECORDING_TRANSCRIPTION_GENERATED Transcription ready
FORM_SUBMITTED Routing form submitted

Webhook Payload

BOOKING_CREATED Payload

{
  "triggerEvent": "BOOKING_CREATED",
  "createdAt": "2024-01-15T10:00:00.000Z",
  "payload": {
    "type": "30min",
    "title": "30 Minute Meeting",
    "description": "Meeting description",
    "startTime": "2024-01-15T10:00:00.000Z",
    "endTime": "2024-01-15T10:30:00.000Z",
    "organizer": {
      "id": 123,
      "name": "Jane Smith",
      "email": "jane@company.com",
      "timeZone": "America/New_York"
    },
    "attendees": [
      {
        "name": "John Doe",
        "email": "john@example.com",
        "timeZone": "America/New_York"
      }
    ],
    "location": "https://cal.com/video/abc123",
    "destinationCalendar": {
      "integration": "google_calendar",
      "externalId": "calendar-id"
    },
    "uid": "booking-uid-123",
    "metadata": {},
    "responses": {
      "name": "John Doe",
      "email": "john@example.com"
    }
  }
}

BOOKING_CANCELLED Payload

{
  "triggerEvent": "BOOKING_CANCELLED",
  "createdAt": "2024-01-15T12:00:00.000Z",
  "payload": {
    "uid": "booking-uid-123",
    "title": "30 Minute Meeting",
    "startTime": "2024-01-15T10:00:00.000Z",
    "endTime": "2024-01-15T10:30:00.000Z",
    "cancellationReason": "Schedule conflict",
    "organizer": {...},
    "attendees": [...]
  }
}

BOOKING_RESCHEDULED Payload

{
  "triggerEvent": "BOOKING_RESCHEDULED",
  "createdAt": "2024-01-15T11:00:00.000Z",
  "payload": {
    "uid": "new-booking-uid",
    "rescheduleUid": "original-booking-uid",
    "title": "30 Minute Meeting",
    "startTime": "2024-01-16T14:00:00.000Z",
    "endTime": "2024-01-16T14:30:00.000Z",
    "reschedulingReason": "Conflict with another meeting",
    "organizer": {...},
    "attendees": [...]
  }
}

Signature Verification

Webhooks include a signature header for verification:

X-Cal-Signature-256: sha256=<signature>

Verification Example (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return `sha256=${expectedSignature}` === signature;
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-cal-signature-256'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook
  const { triggerEvent, payload: data } = req.body;
  // ...
  
  res.status(200).send('OK');
});

Get a Webhook

GET /v2/webhooks/{webhookId}

Update a Webhook

PATCH /v2/webhooks/{webhookId}

Request Body

{
  "active": false,
  "triggers": ["BOOKING_CREATED"]
}

Delete a Webhook

DELETE /v2/webhooks/{webhookId}

Event Type Webhooks

Create webhooks specific to an event type:

List Event Type Webhooks

GET /v2/event-types/{eventTypeId}/webhooks

Create Event Type Webhook

POST /v2/event-types/{eventTypeId}/webhooks
{
  "subscriberUrl": "https://your-app.com/webhook",
  "triggers": ["BOOKING_CREATED"],
  "active": true
}

Organization Webhooks

For organization-wide webhooks:

List Organization Webhooks

GET /v2/organizations/{orgId}/webhooks

Create Organization Webhook

POST /v2/organizations/{orgId}/webhooks

OAuth Client Webhooks

For platform integrations:

List OAuth Client Webhooks

GET /v2/oauth-clients/{clientId}/webhooks

Create OAuth Client Webhook

POST /v2/oauth-clients/{clientId}/webhooks

Custom Payload Templates

Customize webhook payloads using templates:

{
  "subscriberUrl": "https://your-app.com/webhook",
  "triggers": ["BOOKING_CREATED"],
  "payloadTemplate": "{\"event\": \"{{triggerEvent}}\", \"booking_id\": \"{{payload.uid}}\", \"attendee\": \"{{payload.attendees[0].email}}\"}"
}

Available Template Variables

  • {{triggerEvent}} - Event type
  • {{payload.uid}} - Booking UID
  • {{payload.title}} - Event title
  • {{payload.startTime}} - Start time
  • {{payload.endTime}} - End time
  • {{payload.organizer.name}} - Organizer name
  • {{payload.organizer.email}} - Organizer email
  • {{payload.attendees[0].name}} - First attendee name
  • {{payload.attendees[0].email}} - First attendee email

Best Practices

  1. Always verify signatures: Use the webhook secret to verify payloads
  2. Respond quickly: Return 200 within 5 seconds, process async if needed
  3. Handle retries: Webhooks are retried on failure, implement idempotency
  4. Use HTTPS: Always use HTTPS endpoints for security
  5. Log payloads: Store webhook payloads for debugging
  6. Monitor failures: Track webhook delivery failures

Retry Policy

Failed webhook deliveries are retried with exponential backoff:

  • 1st retry: 1 minute
  • 2nd retry: 5 minutes
  • 3rd retry: 30 minutes
  • 4th retry: 2 hours
  • 5th retry: 24 hours

After 5 failed attempts, the webhook is marked as failed.