🚧 FastCMS is under active development — not ready for production use. APIs and features may change without notice.
FastCMS
Real-time

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.

localhost:8000/admin/webhooks

Webhooks

Manage webhook subscriptions for collection events

Total: 3 webhooks
Page 1 of 1
URLCollectionEventsStatusRetriesCreatedActions
https://hooks.slack.com/services/T00/B00/abc123
posts
createupdate
Enabled32026-02-01
https://api.zapier.com/hooks/catch/1234567/abcdef/
orders
create
Enabled52026-02-10
https://notify.example.com/webhook
users
createdelete
Disabled02026-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:

ParameterRequiredDescription
urlYesThe endpoint that will receive webhook POSTs
collection_nameYesCollection to watch for events
eventsYesArray: ["create", "update", "delete"]
secretNoSecret key for HMAC signature verification
retry_countNoRetry 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:

FieldDescription
eventEvent type: create, update, or delete
collectionCollection name where the event occurred
record_idID of the affected record
dataFull record data (empty for delete events)
timestampWhen 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=5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e

Verify 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 401

Verify 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=posts

Get 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:

AttemptDelay 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_count field 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=50

Response:

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

  1. Respond Quickly — Return 200 OK immediately, process the webhook asynchronously
  2. Verify Signatures — Always verify HMAC signatures in production
  3. Be Idempotent — Handle duplicate webhook deliveries gracefully
  4. Use HTTPS — Only use HTTPS URLs for security
  5. Monitor Failures — Check webhook status regularly and re-enable if disabled

Troubleshooting

ProblemSolution
Webhook not firingCheck active: true, verify collection name and events array
All retries failingCheck delivery logs for error details — connection refused, timeouts, 5xx responses
Signature verification failsEnsure same secret, strip sha256= prefix, hash raw JSON body
Delivery logs emptyLogs only appear after webhook events fire (create/update/delete a record)

On this page