TentoCMS
Api

Webhooks

Events emitted by content write operations, payload shape and retry behaviour.

Status: Complete — All content endpoints trigger webhooks.

Overview

The webhook service is fully integrated with all content CRUD and publish endpoints:

  1. Service: apps/api/src/services/webhook.ts
  2. Routes: apps/api/src/routes/admin/webhooks.ts
  3. Repositories: @tentocms/db (WebhookRepository, WebhookDeliveryRepository)

Available Functions

triggerWebhooks()

Triggers webhooks for a content event. This function:

  • Finds all active webhooks subscribed to the event
  • Creates delivery records in the database
  • Dispatches async HTTP requests via ctx.waitUntil()

Signature:

triggerWebhooks(
  db: D1Database,
  projectId: string,
  event: WebhookEvent,
  data: unknown,
  ctx: ExecutionContext
): Promise<void>

generateSignature()

Generates HMAC-SHA256 signature for payload verification.

processWebhookRetries()

Processes failed webhook deliveries with exponential backoff. Called by cron handler.

Endpoint → Event Mapping

Pages (apps/api/src/routes/pages.ts)

EndpointEventData
POST /admin/pages (create)page.createdCreated page object
PUT /admin/pages/:id (update)page.updatedUpdated page object
POST /admin/pages/:id/resolve-conflictpage.updatedResolved page object
POST /admin/pages/:id/clonepage.createdCloned page object
POST /admin/pages/:id/versions/:v/restorepage.updatedReverted page object
DELETE /admin/pages/:idpage.deleted{ id, name, slug }
POST /admin/pages/:id/publishpage.publishedPublished page object
POST /admin/pages/:id/unpublishpage.unpublishedUnpublished page object
POST /admin/pages/bulk-deletepage.deleted per item{ id } per deleted page
POST /admin/pages/bulk-publishpage.published per item{ id } per published page
POST /admin/pages/bulk-unpublishpage.unpublished per item{ id } per unpublished page

Not triggered for: autosave (too frequent, not a deliberate save), schedule/unschedule (no content change, scheduling metadata only).

Collection Items (apps/api/src/routes/collection-items.ts)

EndpointEventData
POST /admin/collection-items (create)collection_item.createdCreated item object
PUT /admin/collection-items/:id (update)collection_item.updatedUpdated item object
POST /admin/collection-items/:id/resolve-conflictcollection_item.updatedResolved item object
POST /admin/collection-items/:id/clonecollection_item.createdCloned item object
POST /admin/collection-items/:id/versions/:v/restorecollection_item.updatedReverted item object
DELETE /admin/collection-items/:idcollection_item.deleted{ id, name, slug }
POST /admin/collection-items/:id/publishcollection_item.publishedPublished item object
POST /admin/collection-items/:id/unpublishcollection_item.unpublishedUnpublished item object
POST /admin/collection-items/bulk-deletecollection_item.deleted per item{ id } per deleted item
POST /admin/collection-items/bulk-publishcollection_item.published per item{ id } per published item
POST /admin/collection-items/bulk-unpublishcollection_item.unpublished per item{ id } per unpublished item

Not triggered for: autosave (too frequent, not a deliberate save), schedule/unschedule (no content change, scheduling metadata only).

Media (apps/api/src/routes/media.ts)

EndpointEventData
POST /admin/media/uploadmedia.uploadedCreated media object
POST /admin/media/bulk-uploadmedia.uploaded per fileEach media object
DELETE /admin/media/:idmedia.deleted{ id, filename, originalFilename }

Not triggered for: metadata update (PUT /admin/media/:id), move (POST /admin/media/:id/move), folder rename (PUT /admin/media/folders/:name) — no matching event types defined for these operations.

Available Webhook Events

All events are defined in @tentocms/shared:

  • page.created
  • page.updated
  • page.published
  • page.unpublished
  • page.deleted
  • collection_item.created
  • collection_item.updated
  • collection_item.published
  • collection_item.unpublished
  • collection_item.deleted
  • media.uploaded
  • media.deleted

Webhook Payload Structure

All webhooks receive this payload structure:

{
  event: 'page.published',
  timestamp: '2024-01-15T10:30:00.000Z',
  data: {
    // The actual content data (page, collection item, media, etc.)
  }
}

HTTP Headers

Webhook requests include these headers:

  • Content-Type: application/json
  • X-Webhook-Signature: sha256={hmac_signature}
  • X-Webhook-Event: {event_type}
  • X-Webhook-Delivery-Id: {delivery_uuid}
  • User-Agent: TentoCMS-Webhook/1.0
  • Any custom headers configured on the webhook

Retry Logic

Failed deliveries are automatically retried with exponential backoff:

  1. 1 minute
  2. 5 minutes
  3. 15 minutes
  4. 1 hour
  5. 4 hours

After 5 failed attempts, the delivery is marked as exhausted.

Testing

Use the test endpoint to verify webhook configuration:

POST /api/v1/admin/webhooks/:id/test

This sends a test page.updated event with sample data.

Security

  • Secrets are only shown on webhook creation and regeneration
  • Secrets are masked in list views (shows last 8 chars)
  • All webhook operations require developer role
  • HMAC-SHA256 signatures allow receivers to verify authenticity

Performance & Resilience

  • Webhook delivery is async via ctx.waitUntil() — doesn't block API responses
  • All triggerWebhooks() calls are wrapped in try/catch — webhook failures never break content operations
  • Bulk operations fire webhooks in parallel via Promise.allSettled
  • 30 second timeout per delivery attempt
  • Response body captured up to 10KB
  • Cron handler processes retries every minute
Copyright © 2026