8 min read
Jan 03, 2026

Webhooks

Receive real-time notifications when events occur in Pivot using webhooks.

Webhooks allow you to receive real-time notifications when events occur in Pivot. When an event happens, Pivot sends an HTTP POST request to your configured endpoint with details about the event.

Creating a Webhook

To create a webhook, send a POST request to the webhooks endpoint:

Terminal window
POST /v1/organizations/{organization_id}/webhooks
Authorization: Bearer your_api_key
Content-Type: application/json
{
"content": {
"name": "My Webhook",
"description": "Notifications for room messages",
"endpoint_url": "https://your-server.com/webhook",
"subscriptions": [
{
"subject_type": "WEBHOOK_SUBJECT_TYPE_ROOM",
"subject_id": "<room_uuid>",
"subscription_type": "WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_SENT",
"filter": {
"room_type": "chat"
}
}
]
}
}

The response includes the webhook details and a secret for signature verification:

{
"webhook": {
"id": "webhook-uuid",
"organization_id": "org-uuid",
"name": "My Webhook",
"endpoint_url": "https://your-server.com/webhook",
"status": "WEBHOOK_STATUS_ACTIVE",
"subscriptions": [...]
},
"secret": "your-webhook-secret"
}

Important: Store the secret securely. It cannot be retrieved again and is required to verify webhook signatures. Each webhook retains the secret it was created with (which may be shared by other webhooks in the organization).

Event Types

The following event types are available for webhook subscriptions:

Subscription TypeEvent TypeDescription
WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_SENTmessage_sentA message was sent in a room
WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_DELETEDmessage_deletedA message was deleted in a room
WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_EDITEDmessage_editedA message was edited in a room
WEBHOOK_SUBSCRIPTION_TYPE_ROOM_RECORDING_PUBLISHEDroom_recording_publishedA recording video is ready
WEBHOOK_SUBSCRIPTION_TYPE_ROOM_RECORDING_TRANSCRIPT_PUBLISHEDroom_recording_transcript_publishedA recording transcript is ready
WEBHOOK_SUBSCRIPTION_TYPE_BLOCK_RESPONSE_SENTblock_response_sentA block response was submitted

All webhook payloads use camelCase field names. For complete payload schemas and JSON examples for each event type, see the Webhook Event Reference.

Subscription Subjects

Subscriptions define what entities trigger webhook notifications:

Subject TypeDescription
WEBHOOK_SUBJECT_TYPE_ROOMEvents for a specific room
WEBHOOK_SUBJECT_TYPE_SPACEEvents for all rooms in a space
WEBHOOK_SUBJECT_TYPE_BLOCKEvents for a specific block

Filters

Optionally filter events using the filter object:

{
"subject_type": "WEBHOOK_SUBJECT_TYPE_ROOM",
"subject_id": "room-uuid",
"subscription_type": "WEBHOOK_SUBSCRIPTION_TYPE_MESSAGE_SENT",
"filter": {
"room_type": "chat"
}
}

Available filter options:

  • room_type: Filter by room type (e.g., “chat”, “post”, “video”)

Verifying Webhook Signatures

All webhook payloads include security headers:

HeaderDescription
X-Pivot-SignatureHMAC-SHA256 signature of the payload
X-Pivot-EventThe event type (e.g., “message_sent”)
X-Pivot-DeliveryUnique delivery identifier
X-Pivot-Webhook-IdThe ID of the webhook that triggered the delivery

Verification Example

If you’re using the Pivot SDK, signature verification is built in:

import express from 'express';
import { parseWebhookEvent } from '@hellopivot/sdk';
// Use express.raw() so req.body is a Buffer (not a parsed object).
// The raw string is needed for HMAC signature verification.
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const payload = req.body.toString('utf8');
const event = parseWebhookEvent(
payload,
req.headers['x-pivot-signature'],
process.env.WEBHOOK_SECRET
);
// event is fully typed — use event.eventType to handle each case
console.log(event.eventType, event.data);
res.status(200).send('OK');
} catch {
res.status(401).send('Invalid signature');
}
});

If you’re not using the SDK, compute the HMAC-SHA256 manually:

import { createHmac, timingSafeEqual } from 'node:crypto';
function verifySignature(payload, signature, secret) {
const expected =
'sha256=' + createHmac('sha256', secret).update(payload).digest('hex');
return (
Buffer.byteLength(signature) === Buffer.byteLength(expected) &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
);
}

Managing Webhooks

Rotate Secret for Future Webhooks

Terminal window
POST /v1/organizations/{organization_id}/webhooks/secret/rotate

RotateWebhookSecretRequest uses only the organization_id path parameter. No request body is required. The response returns the new secret used for future webhooks.

Use this when you want newly created webhooks to use a new signing secret. Existing webhooks keep their current secret and are not changed by this operation.

List Webhooks

Terminal window
GET /v1/organizations/{organization_id}/webhooks

Optionally filter by webhook name (case-insensitive substring match):

Terminal window
GET /v1/organizations/{organization_id}/webhooks?name=My%20Webhook

Update Webhook

Terminal window
PATCH /v1/organizations/{organization_id}/webhooks/{webhook_id}
{
"content": {
"name": "Updated Name",
"status": "WEBHOOK_STATUS_INACTIVE"
}
}

Delete Webhook

Terminal window
DELETE /v1/organizations/{organization_id}/webhooks/{webhook_id}

View Webhook Logs

Terminal window
GET /v1/organizations/{organization_id}/webhooks/{webhook_id}/logs

Logs are retained for 30 days and include delivery status, response codes, and retry counts.

Best Practices

  1. Respond quickly - Return a 2xx status code within 15 seconds
  2. Verify signatures - Always validate the webhook signature before processing
  3. Use HTTPS - Only use HTTPS endpoints for security
  4. Handle idempotency - Use the X-Pivot-Delivery header to detect duplicate deliveries
  5. Monitor logs - Check webhook logs regularly to debug issues

Retry Policy

Failed webhook deliveries are retried up to 5 times with exponential backoff:

RetryDelay
11 minute
25 minutes
330 minutes
42 hours
524 hours

After all retries are exhausted, organization admins receive an email notification about the failed webhook.

Automatic Pausing

To protect your system and ours, webhooks are automatically paused if they experience sustained failures:

  • Threshold: 10 consecutive event failures (across different events, not retries of the same event)
  • Time window: Failures must occur within a 3-day period
  • Status change: Webhook status changes from WEBHOOK_STATUS_ACTIVE to WEBHOOK_STATUS_PAUSED

When a webhook is paused:

  1. Organization admins receive an email notification explaining why the webhook was paused
  2. No further delivery attempts are made until the webhook is reactivated
  3. The failure counter has a 3-day expiration (TTL). After 3 days, the counter is automatically cleared, but the webhook remains paused — reactivation requires manual action by an organization admin (see below)

Reactivating a Paused Webhook

To reactivate a paused webhook, update its status to active:

Terminal window
PATCH /v1/organizations/{organization_id}/webhooks/{webhook_id}
{
"content": {
"status": "WEBHOOK_STATUS_ACTIVE"
}
}

Before reactivating, ensure your endpoint is functioning correctly to avoid the webhook being paused again.

Authorization

ActionRequired Role
List webhooksOrganization reader role
Create/Update/Delete webhooksOrganization admin or super_admin

When creating subscriptions, you must be an organization admin (or super admin). Subject-specific room, block, or space access is not required.

Was this guide helpful?