Back to Cal

Webhooks API Reference

agents/skills/calcom-api/references/webhooks.md

6.2.07.3 KB
Original Source

Webhooks API Reference

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

Endpoints Overview

MethodEndpointDescription
GET/v2/webhooksList webhooks
POST/v2/webhooksCreate 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

http
GET /v2/webhooks

Response

json
{
  "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

http
POST /v2/webhooks

Request Body

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

Fields

FieldTypeRequiredDescription
subscriberUrlstringYesURL to receive webhook payloads
triggersarrayYesEvents that trigger the webhook
activebooleanNoEnable/disable webhook (default: true)
payloadTemplatestringNoCustom payload template
secretstringNoSecret for signature verification

Webhook Triggers

TriggerDescription
BOOKING_CREATEDNew booking created
BOOKING_CANCELLEDBooking cancelled
BOOKING_RESCHEDULEDBooking rescheduled
BOOKING_CONFIRMEDPending booking confirmed
BOOKING_REJECTEDBooking rejected
BOOKING_REQUESTEDBooking request received (requires confirmation)
BOOKING_PAYMENT_INITIATEDPayment started
BOOKING_NO_SHOW_UPDATEDAttendee marked as no-show
MEETING_STARTEDVideo meeting started
MEETING_ENDEDVideo meeting ended
RECORDING_READYMeeting recording available
INSTANT_MEETINGInstant meeting created
RECORDING_TRANSCRIPTION_GENERATEDTranscription ready
FORM_SUBMITTEDRouting form submitted

Webhook Payload

BOOKING_CREATED Payload

json
{
  "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": "[email protected]",
      "timeZone": "America/New_York"
    },
    "attendees": [
      {
        "name": "John Doe",
        "email": "[email protected]",
        "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": "[email protected]"
    }
  }
}

BOOKING_CANCELLED Payload

json
{
  "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

json
{
  "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)

javascript
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

http
GET /v2/webhooks/{webhookId}

Update a Webhook

http
PATCH /v2/webhooks/{webhookId}

Request Body

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

Delete a Webhook

http
DELETE /v2/webhooks/{webhookId}

Event Type Webhooks

Create webhooks specific to an event type:

List Event Type Webhooks

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

Create Event Type Webhook

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

Organization Webhooks

For organization-wide webhooks:

List Organization Webhooks

http
GET /v2/organizations/{orgId}/webhooks

Create Organization Webhook

http
POST /v2/organizations/{orgId}/webhooks

OAuth Client Webhooks

For platform integrations:

List OAuth Client Webhooks

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

Create OAuth Client Webhook

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

Custom Payload Templates

Customize webhook payloads using templates:

json
{
  "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.