Webhooks
Subscribe to collection events and receive HTTP callbacks when records are created, updated, or deleted. Includes HMAC signature verification and automatic retry logic.
Webhooks
FastCMS includes a powerful webhook system that allows you to subscribe to events and receive HTTP callbacks when records are created, updated, or deleted in your collections.
Webhooks
Manage webhook subscriptions for collection events
| URL | Collection | Events | Status | Retries | Created | Actions |
|---|---|---|---|---|---|---|
https://hooks.slack.com/services/T00/B00/abc123 | posts | createupdate | Enabled | 3 | 2026-02-01 | |
https://api.zapier.com/hooks/catch/1234567/abcdef/ | orders | create | Enabled | 5 | 2026-02-10 | |
https://notify.example.com/webhook | users | createdelete | Disabled | 0 | 2026-02-20 |
Use Cases
- Send notifications when new users register
- Sync data to external systems
- Trigger workflows in other applications
- Update search indexes when content changes
- Send emails or SMS notifications
- Log events to analytics platforms
Creating a Webhook
Endpoint: POST /api/v1/webhooks
curl -X POST "http://localhost:8000/api/v1/webhooks" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"url": "https://myapp.com/api/webhook",
"collection_name": "posts",
"events": ["create", "update"],
"secret": "myWebhookSecret123",
"retry_count": 3
}'Parameters:
| Parameter | Required | Description |
|---|---|---|
url | Yes | The endpoint that will receive webhook POSTs |
collection_name | Yes | Collection to watch for events |
events | Yes | Array: ["create", "update", "delete"] |
secret | No | Secret key for HMAC signature verification |
retry_count | No | Retry attempts on failure (default: 3, max: 10) |
Webhook Payload
When an event occurs, FastCMS sends a POST to your URL:
{
"event": "create",
"collection": "posts",
"record_id": "record-uuid",
"data": {
"id": "record-uuid",
"title": "New Post",
"content": "Post content...",
"created": "2025-01-15T10:00:00",
"updated": "2025-01-15T10:00:00"
},
"timestamp": "2025-01-15T10:00:00"
}Fields:
| Field | Description |
|---|---|
event | Event type: create, update, or delete |
collection | Collection name where the event occurred |
record_id | ID of the affected record |
data | Full record data (empty for delete events) |
timestamp | When the event occurred (ISO 8601) |
Webhook Security (HMAC Signatures)
If you provided a secret, FastCMS signs each request with HMAC-SHA256. The signature is sent in the X-Webhook-Signature header with a sha256= prefix:
X-Webhook-Signature: sha256=5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2eVerify in Python:
import hmac
import hashlib
def verify_webhook(request_body: bytes, signature_header: str, secret: str) -> bool:
# Strip the "sha256=" prefix
received_sig = signature_header.removeprefix("sha256=")
expected = hmac.new(
secret.encode(),
request_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_sig)
# In your webhook handler:
signature = request.headers.get('X-Webhook-Signature')
if not verify_webhook(request.body, signature, 'myWebhookSecret123'):
return 401Verify in Node.js:
const crypto = require('crypto');
function verifyWebhook(body, signatureHeader, secret) {
const receivedSig = signatureHeader.replace('sha256=', '');
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(receivedSig)
);
}Managing Webhooks
List All Webhooks
GET /api/v1/webhooks
# Filter by collection
GET /api/v1/webhooks?collection_name=postsGet Single Webhook
GET /api/v1/webhooks/{webhook_id}Update Webhook
PATCH /api/v1/webhooks/{webhook_id}
# Request Body (all fields optional)
{
"url": "https://new-url.com/webhook",
"events": ["create"],
"active": false,
"secret": "newSecret",
"retry_count": 5
}Delete Webhook
DELETE /api/v1/webhooks/{webhook_id}Retry Logic & Exponential Backoff
When your endpoint returns a 5xx error or a connection fails, FastCMS retries with exponential backoff:
| Attempt | Delay Before |
|---|---|
| 1st (initial) | immediate |
| 2nd (retry 1) | 1 second |
| 3rd (retry 2) | 2 seconds |
| 4th (retry 3) | 4 seconds |
| 5th (retry 4) | 8 seconds |
| ... | doubles each time, capped at 30s |
- 4xx errors are treated as permanent failures (bad request, not found, etc.) and are not retried
- Connection errors (timeout, refused) are retried
- The
retry_countfield controls how many retries (0 = no retries, max 10)
Delivery Logs
Every webhook delivery attempt is logged with status code, response body, duration, and any errors. View delivery history via the admin UI ("Logs" button per webhook) or the API:
# Delivery logs for a specific webhook
GET /api/v1/webhooks/{webhook_id}/deliveries?skip=0&limit=50
# Recent deliveries across all webhooks
GET /api/v1/webhooks/deliveries/recent?skip=0&limit=50Response:
{
"items": [
{
"id": "delivery-uuid",
"webhook_id": "webhook-uuid",
"event_type": "create",
"record_id": "record-uuid",
"url": "https://myapp.com/webhook",
"status_code": 200,
"response_body": "OK",
"attempt": 1,
"success": true,
"duration_ms": 142,
"error": null,
"created": "2026-03-14T10:00:00"
}
],
"total": 1
}Best Practices
- Respond Quickly — Return
200 OKimmediately, process the webhook asynchronously - Verify Signatures — Always verify HMAC signatures in production
- Be Idempotent — Handle duplicate webhook deliveries gracefully
- Use HTTPS — Only use HTTPS URLs for security
- Monitor Failures — Check webhook status regularly and re-enable if disabled
Troubleshooting
| Problem | Solution |
|---|---|
| Webhook not firing | Check active: true, verify collection name and events array |
| All retries failing | Check delivery logs for error details — connection refused, timeouts, 5xx responses |
| Signature verification fails | Ensure same secret, strip sha256= prefix, hash raw JSON body |
| Delivery logs empty | Logs only appear after webhook events fire (create/update/delete a record) |