Files
calendar/agents/skills/calcom-api/references/webhooks.md
T
Eunjae LeeGitHubClaude Haiku 4.5Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
12abc316e6 refactor: consolidate agent config into agents directory (#27427)
* refactor: consolidate agent config folders into agents directory

- Move .claude/skills and .claude/rules content to agents/
- Remove duplicate .cursor/ and .goose/ folders
- Create symlinks from .claude/ and .cursor/ to agents/
- Convert review.mdc to quality-review-checklist.md with proper frontmatter

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add Cal.com APIv2 skills to agents directory

Move the Cal.com API v2 skills from PR #27445 into the consolidated
agents/skills/ directory structure. This includes:
- SKILL.md - Main skill file with API overview
- references/authentication.md - Authentication methods
- references/bookings.md - Bookings API reference
- references/calendars.md - Calendars API reference
- references/event-types.md - Event types API reference
- references/schedules.md - Schedules API reference
- references/slots-availability.md - Slots and availability API reference
- references/webhooks.md - Webhooks API reference

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: update calcom-api skill to use Claude Code frontmatter format

Remove non-standard frontmatter fields (license, metadata) and keep only
the Claude Code supported fields (name, description) as per the
Claude Code skills specification.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: restructure calcom-api SKILL.md as index file

Convert SKILL.md from a comprehensive API doc (317 lines) to a concise
index file (108 lines) that references the detailed documentation in
the references/ folder. This follows the Claude Code skills pattern of
keeping SKILL.md focused with supporting files for detailed content.

The SKILL.md now:
- Provides a quick start guide with essential examples
- References all 7 detailed reference docs in a table
- Lists common workflows and best practices
- Points to external resources

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* docs: preserve deleted content in reference files

Move content that was removed from SKILL.md to appropriate reference files:
- Add Error Handling and Pagination sections to authentication.md
- Add Organization endpoints to event-types.md
- Add Core Concepts section back to SKILL.md

This ensures no useful API documentation is lost during the restructuring.

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-03 14:14:41 +01:00

7.3 KiB

Webhooks API Reference

Detailed documentation for webhook management endpoints in the Cal.com 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.