Webhooks overview

Webhooks let Callr notify your server when something happens on your account: a call starts or ends, an SMS is received, a recording is ready, your prepaid credit runs low... Subscribe with Create webhook: one webhook per event type and endpoint.

Delivery

Callr sends an HTTP POST request to your endpoint, with a JSON body (Content-Type: application/json, User-Agent: Callr API). HTTP redirects are followed.

HeaderContent
X-Callr-EventIdUnique event ID, same as id in the body
X-Callr-HmacSignatureBase64 HMAC signature of the raw body, only when the webhook has options.hmac (see below)

Every event has the same envelope:

FieldDescription
idUnique event ID. Use it to deduplicate events.
atDate of the event (RFC 3339)
sidSID of the webhook
typeEvent type, e.g. v2.call.inbound.ended
tryDelivery attempt, starting at 0
payloadThe event data, described on each event page
{
  "id": "a0331744032581221927Lm4RtYvB8nQe",
  "at": "2025-04-07T13:29:41.221Z",
  "sid": "p7w2dz5c",
  "type": "v2.call.inbound.ended",
  "try": 0,
  "payload": { ... }
}

Responding

Return any 2xx status code. Anything else is a failure: another status code, a connection error, or no response within 10 seconds (5 seconds to connect).

👍

Respond first, process later

Store the event and reply 200 right away, then process it asynchronously.

Retries

A failed delivery is retried, up to 10 attempts in total (try from 0 to 9). After attempt n, Callr waits n × 10 seconds (10 s, 20 s... 90 s): about 7.5 minutes overall. Every attempt carries the same id.

Events can arrive more than once, and out of order: deduplicate them on id, and order them with at.

Verifying the signature

When you create a webhook with options.hmac (secret and algorithm: sha256 or sha512), every request carries an X-Callr-HmacSignature header: the base64-encoded HMAC of the request body, computed with your secret.

To verify it, compute the HMAC over the raw body (the bytes you received, before parsing the JSON), encode it in base64, and compare it to the header in constant time:

import crypto from 'node:crypto';

// rawBody: the request body as received (Buffer), before JSON parsing
// signature: req.headers['x-callr-hmacsignature']
function isValidCallrSignature(rawBody, signature, secret, algorithm = 'sha256') {
  const expected = Buffer.from(
    crypto.createHmac(algorithm, secret).update(rawBody).digest('base64')
  );
  const received = Buffer.from(signature || '');
  return expected.length === received.length && crypto.timingSafeEqual(expected, received);
}

See the recipe Create a webhook and verify its signature for a complete walkthrough.

Automatic deactivation

Every failed attempt counts. After 500 failed attempts, the account contact and technical emails receive a warning. After 1,000 failed attempts, the webhook is disabled (status disabled) and an email is sent.

The counter goes back to zero when you re-enable the webhook (Update webhook with status: enabled), or after 60 days without any failure.

The statuses disabled-admin and disabled-system mean that the webhook was disabled by Callr: please contact our support team.

Logs

Every delivery attempt, with the HTTP status code returned by your endpoint and its duration, is available with Search webhook logs.

Events

EventTriggered when
v2.audio-file.updatedAn audio file has an updated status
v2.billing.credit.topupYour prepaid account is credited
v2.billing.credit.warningYour prepaid balance reaches its warning threshold
v2.billing.credit.shutdownYour prepaid balance reaches its shutdown threshold
v2.call.inbound.startedAn inbound call starts
v2.call.inbound.endedAn inbound call ends
v2.call.outbound.startedAn outbound call starts
v2.call.outbound.endedAn outbound call ends
v2.number.assignedA phone number is assigned to your account
v2.number.unassignedA phone number is unassigned from your account
v2.recording.updatedA recording has an updated status
v2.sms.inbound.receivedAn inbound SMS is received
v2.sms.outbound.updatedAn outbound SMS has an updated status
v2.task.updatedA task has an updated status