Skip to content
Zumu

Developers · Webhooks

A call event that fires is not the same as one that arrives.

Zumu’s voice AI webhooks sign every delivery, retry the ones that fail, and put a person in the loop the moment retries run out, so the callback your CRM was counting on does not just vanish into a 500 nobody noticed.

  • HMAC signed, every delivery
  • Secret rotation with overlap
  • Dead-letter instead of a silent drop
  • Replay from the dashboard or the API

The delivery ledger

What happened to two events, in full

One endpoint recovered on its own. The other needed a person to notice. Below is the same attempt-by-attempt record you would see for either one in your own deliveries viewer.

call.flaggedevt_8f21ac09Delivered on retry
  1. Attempt 1POST /webhooks/zumu500
  2. Backoff30s before the next attempt
  3. Attempt 2POST /webhooks/zumu200 OK
  4. VerifiedHMAC-SHA256 against X-Zumu-SignatureMatched the previous secret, inside its rotation overlap window
handoff.initiatedevt_2c74e610Delivered by replay
  1. Attempt 1POST /webhooks/zumu500
  2. Attempt 2POST /webhooks/zumuTimeout
  3. Attempt 3POST /webhooks/zumu500
  4. Retries exhaustedMoved to the dead-letter queueVisible in the deliveries viewer, not dropped
  5. ReplayedAn operator resent it from the dashboard
  6. Attempt 4POST /webhooks/zumu200 OK

One endpoint recovered on its own. The other needed a person to notice and resend it. Neither event just disappeared.

Signing and rotation

A payload you can't verify is a payload you can't trust

Every delivery is signed. Rotating the secret does not mean picking a day to break verification.

  • Every delivery is signed

    Each request carries an HMAC signature in X-Zumu-Signature, alongside X-Zumu-Event-Id, X-Zumu-Event-Type, and X-Zumu-Schema-Version. Compute the same signature over the raw request body with your endpoint secret and compare it before you trust the payload.

  • Rotate a secret without a gap

    Generate a new secret whenever you need to. During the rotation overlap window, a delivery verifies successfully against either the current or the previous secret, so you can update your endpoint on your own schedule instead of racing a hard cutover.

  • Configurable payload profiles

    Each endpoint can shape its own payload profile, so you receive the fields your integration actually needs instead of parsing a payload built for someone else.

Retries, dead-letter, replay

A down endpoint is not a lost event

Zumu assumes your endpoint will fail sometimes and builds the delivery pipeline around that, instead of dropping the event the moment it does.

  • Failures retry with backoff

    A 500, a timeout, or a dropped connection counts as a failed attempt, not a lost event. Zumu retries the delivery with increasing backoff between attempts.

  • Exhausted retries land in a dead-letter state

    When an endpoint stays down long enough that retries run out, the event does not vanish. It moves to a dead-letter state, visible in the deliveries viewer and on the webhooks tab of the call it belongs to.

  • Replay from the dashboard or the API

    A dead-lettered delivery, or any past delivery, can be resent on demand. An operator who just fixed the endpoint does not have to wait for the next real call to prove the fix worked.

What you can subscribe to

Events, grouped by what they tell you

A representative set, not an exhaustive schema reference. Every event here carries the same signature and delivery guarantees described above.

  • Call lifecycle

    • call.started
    • call.ended
    • call.completed

    The moments most integrations subscribe to first: a call begins, a call ends, and a call finishes processing with its full record attached.

  • Call intelligence

    • call.flagged

    Fires when a call matches one of your flag rules, graded by an LLM rather than a fixed keyword list. Covered in its own section below.

  • Handoff and tools

    • handoff.initiated
    • tool.called
    • transcript.updated

    The events that let an external system follow what the agent is doing mid-call: a transfer starting, a tool firing, or the transcript gaining a new turn.

  • Operational

    • error.occurred
    • webhook.test
    • carrier.updated

    Housekeeping events: a runtime error worth knowing about, a manual test delivery you can trigger yourself, and a change on the carrier side of a call.

LLM-graded flag rules

Subscribe to a sentence, not a keyword list

The rule on the right is a real example of the kind of thing you can write, not a hypothetical.

Write the rule in plain language
A flag rule is a sentence, not a query. "The caller sounded ready to cancel" or "the agent could not verify the caller’s identity" is a complete rule on its own.
An LLM grades every call against it
Each rule carries a severity of info, warn, or critical. An LLM reads the call against the rule you wrote and decides whether it matches, the way a person skimming a transcript would.
A match fires call.flagged
When a call matches, it fires the same call.flagged event your other integrations already listen for, with the rule and severity attached, so a flagged call reaches the same webhook pipeline as everything else.

Flag rule

The caller sounded ready to cancel

warncall.flagged

This is a judgment call from a model reading a transcript, not a certified accuracy figure. Treat a flag as a reason to look, not a verdict.

Verifying a delivery

The whole check, in one function

Swap in your framework's raw-body access and this runs as written. No package to install.

Nodeverify-zumu-webhook.js · Node, no dependencies
import crypto from 'node:crypto'// secrets = [currentSecret, previousSecret] inside a rotation overlap windowfunction isValidSignature(rawBody, signature, secrets) {  return secrets.some((secret) => {    const expected = crypto      .createHmac('sha256', secret)      .update(rawBody)      .digest('hex')    const expectedBuffer = Buffer.from(expected, 'hex')    const signatureBuffer = Buffer.from(signature, 'hex')    return (      expectedBuffer.length === signatureBuffer.length &&      crypto.timingSafeEqual(expectedBuffer, signatureBuffer)    )  })}

Reject the delivery if isValidSignature returns false. Do not parse the body into JSON before you verify it, since re-serializing it can change the bytes the signature was computed over.

Questions

The delivery mechanics, answered plainly

Every delivery carries an HMAC-SHA256 signature in the X-Zumu-Signature header, computed over the raw request body with your endpoint secret. Recompute it on your side and compare it with a constant-time check before you trust the payload. The verification section above has a runnable example.

Book a demo

Bring us the webhook that failed quietly

Show us the integration where a call event never showed up, and nobody knew until a customer complained. We will wire up the same event with a signed delivery, real retries, and a dead-letter queue you can actually see, while you watch it happen.

  • No credit card for the demo
  • You see every delivery attempt, not just the successful ones
Zumu Webhooks | Signed, Retried, Replayable Call Events