Skip to content

Webhooks

Webhooks let you receive real-time HTTP notifications when events occur in your project. Configure a webhook URL and select which events to listen for — Studio will POST a signed payload to your endpoint whenever a matching event fires.

Managed service / Enterprise Edition

Outbound webhooks are an Enterprise Edition feature, gated by the api.webhooks_outbound plan feature. They are available on Contentrain's managed service (Starter plans and above) and on self-hosted Enterprise Edition deployments. They are not available in the self-hosted Community Edition.

Creating a Webhook

  1. Open the project sidebar and navigate to webhook settings
  2. Click Create webhook
  3. Enter the webhook URL (must be HTTPS for production)
  4. Select the events you want to subscribe to
  5. A webhook secret is generated automatically for signature verification

Creating a webhook: name, endpoint URL, and the event checkboxes for all eight event types

WARNING

Webhook URLs must point to public endpoints. Studio blocks requests to private networks (RFC 1918), localhost, link-local addresses, and cloud metadata endpoints for SSRF protection.

Event Types

Studio supports eight webhook event types:

EventFires When
content.savedA content entry is created or updated
content.deletedA content entry is deleted
model.savedA content model is created or updated
branch.mergedA review branch is merged to main
branch.rejectedA review branch is rejected (deleted)
cdn.build_completeA CDN build finishes successfully
media.uploadedA media asset is uploaded
form.submittedA public form submission is received

Event Payload Format

All webhook payloads follow the same structure:

json
{
  "event": "content.saved",
  "projectId": "project-uuid",
  "timestamp": "2026-04-02T10:30:00.000Z",
  "data": {
    "models": ["blog-posts"],
    "locale": "en",
    "source": "api"
  }
}

The envelope is always { event, projectId, timestamp, data }. The data field varies by event type and contains event-specific information. Most events include a source field (api, conversation, etc.) indicating how the change was made.

Event-Specific Data

content.saved:

json
{
  "models": ["blog-posts"],
  "locale": "en",
  "source": "api"
}

content.deleted:

json
{
  "models": ["blog-posts"],
  "locale": "en",
  "entryIds": ["a1b2c3d4e5f6", "g7h8i9j0k1l2"],
  "source": "conversation"
}

model.saved:

json
{
  "modelId": "blog-posts",
  "source": "api"
}

branch.merged:

json
{
  "branch": "cr/content/blog-posts/en/1774800862-27c1",
  "source": "api"
}

cdn.build_complete:

json
{
  "buildId": "build-uuid",
  "status": "success",
  "filesUploaded": 15,
  "durationMs": 3200,
  "error": null
}

media.uploaded:

json
{
  "assetId": "asset-uuid",
  "filename": "hero-banner.webp",
  "contentType": "image/webp"
}

form.submitted:

json
{
  "submissionId": "submission-uuid",
  "modelId": "contact-form",
  "status": "pending"
}

Webhook Security

HMAC-SHA256 Signatures

Every webhook delivery includes a signature in the X-Contentrain-Signature header. The signature is an HMAC-SHA256 hash of the request body using your webhook secret.

To verify a webhook delivery:

javascript
import { createHmac, timingSafeEqual } from 'crypto'

function verifyWebhook(body, signature, secret) {
  const expected = createHmac('sha256', secret)
    .update(body)
    .digest('hex')

  return timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  )
}

WARNING

Always verify webhook signatures before processing the payload. Use timing-safe comparison to prevent timing attacks.

SSRF Protection

Studio validates webhook URLs to prevent Server-Side Request Forgery:

  • Blocked: localhost, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1, cloud metadata endpoints
  • Required: http:// or https:// protocol
  • Recommended: Use HTTPS in production

Test Delivery

You can send a test delivery to verify your webhook configuration:

  1. Open the webhook in settings
  2. Click Test
  3. Studio sends a test payload to your endpoint
  4. View the delivery result (status code, response time)

Delivery Logs

Every webhook delivery is logged. View the delivery history to see:

FieldDescription
Event typeWhich event triggered the delivery
Status codeHTTP response code from your endpoint
Response timeHow long the delivery took
TimestampWhen the delivery was attempted
PayloadThe data that was sent

Retry Behavior

If a webhook delivery fails (non-2xx response or timeout), Studio retries with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
612 hours

After 5 failed retries, the delivery is marked as permanently failed. You can view failed deliveries in the delivery logs.

TIP

Ensure your webhook endpoint responds quickly (under 10 seconds) with a 2xx status code. Return 200 OK as soon as you receive the payload, then process it asynchronously.

Managing Webhooks

Editing a Webhook

You can update a webhook's URL, events, or active status:

  1. Open webhook settings
  2. Click on the webhook to edit
  3. Modify the URL or event subscriptions
  4. Save changes

Deleting a Webhook

To remove a webhook:

  1. Open webhook settings
  2. Click the delete button on the webhook
  3. Confirm the deletion

Deleting a webhook stops all future deliveries immediately.

Plan Limits

FeatureFreeStarterProEnterprise
Outbound webhooks0310Unlimited

Use Cases

ScenarioEventsAction
Rebuild static sitebranch.mergedTrigger CI/CD pipeline
Notify Slack on new contentcontent.savedPost to Slack channel
Invalidate CDN cachecdn.build_completePurge cache keys
Process form submissionsform.submittedSend to CRM or email
Sync search indexcontent.saved, content.deletedUpdate Algolia/Typesense
Audit trailAll eventsLog to monitoring service

Next Steps

  • CDN — Deliver content through the built-in CDN
  • Forms — Accept and manage form submissions
  • Conversation API — Programmatic content management

Released under the AGPL-3.0 License.