This contract applies only to v2 subscriptions. Existing legacy outbound webhooks keep their own payloads and
signatures; do not replace a legacy verifier with the code below.
Register and verify a receiver
The credential needswebhooks:manage and the read scope for every selected
resource event. Persist a subscription UUID, then PUT /webhooks/{id} with an
idempotency key and this shape:
POST /webhooks/{id}/rotate-secretreturns{id,signingSecret,active:false}. Store this one-time secret securely and configure your receiver with it.POST /webhooks/{id}/verifysends a signed{type:"webhook.verification",challenge:"random value"}request.- Verify the raw-body signature, then return 2xx JSON containing the identical
challengewithin 15 seconds. Successful verification enables the subscription.
{active:false} pauses; PATCH {active:true}
requires prior verification. Requests already in flight may still arrive.
Verify signatures before parsing
PowerDialer-Signature has the format t=<Unix milliseconds>,v1=<hex HMAC-SHA256>.
Sign timestamp + . + the exact raw request bytes with the literal signing-secret
string. Do not parse and reserialize JSON before verification. Compare in constant
time and reject timestamps more than five minutes past or future.
Event identity and payload
id, or subscription
plus event ID for per-subscription processing. PowerDialer-Delivery-Id stays
stable per subscription/event; PowerDialer-Attempt-Id changes on each send.
The catalog includes contact/list create, update, delete and access_revoked
events; list.membership_updated; call create, update and delete events;
call.completed, call.disposition_updated, recording.available, and
transcript.available. Treat deletes and access revocations as removals from the
corresponding owner’s local view. Re-fetch permitted current state when processing
other events. Generic and specialized events can describe the same mutation.
Delivery order is not a resource version, and a completed call need not have all
recordings or transcripts ready.
Delivery history and replay
Transient network errors, 408, 429 and 5xx retry automatically up to eight attempts. Exponential delay starts at 30 seconds plus jitter, capped at 3,600 seconds. Other non-2xx responses fail without automatic retry. Requests time out at 15 seconds and do not follow redirects. A timeout may occur after the receiver accepted work.GET /webhooks/{id}/deliveries: latest 100 deliveries and current states.GET /webhooks/{id}/deliveries/{deliveryId}/attempts: latest 100 attempts.POST /webhooks/{id}/deliveries/{deliveryId}/replay: requires an idempotency key and active verified subscription. Returns 202{id,eventId,state:"pending"}. This means queued, not delivered. An active attempt can return 409; wait before explicitly retrying.
Recover with the change feed
GET /changes?after=0&limit=50 requires changes:read and the resource read scopes
for the events you need. It returns {data,nextCursor,hasMore}. Each change has a
publication sequence represented as a decimal string, plus event ID, workspace
(orgId), owner, type, resource reference and timestamp.
Never convert the cursor to a JavaScript number. Persist it only after all events
in its page are durably processed or queued. Cursors are assigned after commit;
a late transaction receives a later cursor. Keep polling after hasMore:false:
that only describes currently published visible events. Unlike collection pages,
nextCursor remains present when a change page is empty.
For initial sync, record changes from cursor zero, enumerate visible resources,
then drain changes and continue polling. Merge by stable resource ID. A new webhook
subscription starts after the latest published workspace cursor; earlier pending
events may still publish afterward. Webhooks do not backfill published history.
Event capture requires a non-revoked, unexpired workspace API key at mutation time.
After a period with no active keys, enumerate again and reconcile local removals:
missing historical events cannot be replayed. Historical record availability may
also require a controlled backfill; see coverage.
No automatic event pruning is currently configured. That is not an unlimited
retention commitment; a retention/resynchronization policy must accompany any
future pruning.