Safe Creative Platform

7. Webhooks

Configure endpoints in the console and pick what to receive: certification.completed / .failed, balance.low, balance.exhausted, events.unpaid_limit_reached, consumption.limit_reached, recharge.auto_completed / .auto_failed. From the console you can also request a test delivery (webhook.test) to any endpoint.

Every delivery shares the same JSON envelope: id (the same on retries), type, created_at, mode (live or test: balance and limit notices are always live; certification notices carry the certification's mode) and data, whose model depends on type:

type Sent when data fields
certification.completed The certification has been issued certification_id, channel_id, status, event_count, verification_code
certification.failed The certification could not be issued (the charge is refunded) certification_id, channel_id, status, event_count, incident_id
balance.low The balance drops below an alert threshold (once per crossing) threshold_id, threshold, balance
balance.exhausted The first event is registered as unpaid channel_id
events.unpaid_limit_reached The unpaid-events limit is reached: the API rejects registrations with 402 unpaid_limit, unpaid_events, unpaid_credits
consumption.limit_reached A consumption limit reaches its maximum for the period limit_id, scope, channel_id, key_id, period, period_key, max_events, action
recharge.auto_completed An automatic top-up was charged and its credits are in the balance recharge_id, pack_id, credits, amount, currency, threshold, balance
recharge.auto_failed An automatic top-up failed and was turned off recharge_id, pack_id, credits, amount, currency, threshold, reason, payment_url, auto_recharge_disabled
webhook.test You request a test delivery in the console endpoint_id, message
{
  "id": "ntc_…",
  "type": "balance.low",
  "created_at": "2026-09-28T07:00:16.526317Z",
  "mode": "live",
  "data": { "threshold_id": "thr_…", "threshold": 1000, "balance": 999 }
}

Credits are integers and timestamps are UTC with six fractional digits. Each field is described in the API reference (Webhook…Data schemas). We may add fields to data over time: ignore the ones you do not know.

Every delivery is signed with the endpoint secret and retried with exponential backoff for up to 24 hours (1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h). The delivery log lives in the console; an endpoint that keeps failing is disabled and we notify you by email.

How to verify a delivery before processing it:

  1. Read the X-SC-Timestamp (Unix seconds) and X-SC-Signature (v1=<hex>) headers.
  2. Compute HMAC-SHA256 with your secret (whsec_…) over <X-SC-Timestamp>.<raw body> and compare it with <hex> in constant time.
  3. Reject timestamps older than 5 minutes.
  4. Reply with any 2xx; de-duplicate by X-SC-Webhook-Id (the envelope id, the same on retries).