Webhooks deliver platform events to your endpoint as signed HTTPS
POSTs. One subscription covers one or more event types; Yotel fans
out each event to every matching subscription.
Subscribe
Subscriptions are managed via the dashboard (Supabase JWT auth), not
via the /api/v1/* path. We decided early on that webhook management
is a human operation that shouldn’t be scripted by customer backends.
- Dashboard → Developer → Webhooks
- Click New subscription
- Enter your HTTPS URL (HTTP is rejected at the DB layer)
- Pick event types from the catalog
- On save, copy the
signing_secret — shown once
You can have multiple subscriptions — e.g. one for call.ended to
your warehouse, another for campaign.paused to your on-call Slack.
Event envelope
Every delivery is a POST with:
POST /your-url HTTP/1.1
Content-Type: application/json
X-Zetta-Event: call.ended
X-Zetta-Delivery: 3c8f4a9b-7d11-4c8d-9a2e-1b3e5f6a7c12
X-Zetta-Timestamp: 1744934400
X-Zetta-Signature: sha256=abc123…
{
"event_id": "1f2e...",
"event_type": "call.ended",
"timestamp": 1744934400,
"data": {
"call_id": "c-1234",
"campaign_id": "camp-567",
"lead_id": "l-890",
"hangup_cause": "NORMAL_CLEARING",
"talk_duration_s": 42,
"disposition": "interested",
"recording_url": "https://storage.googleapis.com/yotel-recordings/..."
}
}
Expectations for your endpoint
| Requirement |
|---|
| Protocol | HTTPS — http:// URLs are rejected at subscribe time |
| Timeout | Respond within 10s — we cancel the request after that |
| Success | Return 2xx to mark the delivery delivered |
| Temporary failure | Return 408, 429, or 5xx → we retry |
| Permanent failure | Return other 4xx → we dead-letter immediately (no retry) |
| Ordering | None guaranteed — key on event_id for idempotent processing |
Retries
Failed deliveries retry on this schedule (±10% jitter):
| Attempt | Delay |
|---|
| 1 → 2 | +5s |
| 2 → 3 | +30s |
| 3 → 4 | +3m |
| 4 → 5 | +15m |
| 5 → 6 | +1h |
| 6 → 7 | +6h |
| 7 | DEAD — no more retries |
Total retry window: ~7.5 hours. If your endpoint is down longer
than that for a specific event, it’s gone from the queue — you’ll
need to reconcile state from your side using the relevant REST
endpoints.
After 20 consecutive failures AND no successful delivery in 24h
your subscription is auto-disabled. You’ll need to manually re-enable
from the dashboard.
Troubleshooting
Your dashboard’s Webhooks → Delivery log shows every delivery
attempt with HTTP status, response snippet, and duration. If signatures
don’t verify on your side, 95% of the time it’s clock skew — see
signature verification.
Next