Skip to navigation

Webhooks (new experience)

Signed, retried, replayable delivery of call events to your endpoints, with per-delivery history and endpoint health.

View as Markdown

Rolling out: the new webhooks experience is rolling out by organization. If your dashboard still shows the older Webhooks page, your organization hasn’t been moved yet. Keep using the current Webhooks guide until it switches over.

Webhooks send an HTTP POST to your endpoint whenever something happens on a call. The new experience adds:

  • Signed requests with rotating secrets and replay protection.
  • Automatic retries: up to 7 attempts over about 31 hours.
  • Per-delivery history: every attempt, status code, response body, and the exact payload sent.
  • One-click retry and bulk replay for missed events.
  • Per-agent field selection, so you only receive the data you need.
  • Endpoint health and performance charts, plus an audit log of every change.

Events

EventWhen it firesContains
pre-conversationBefore the agent speaksCaller and callee numbers
post-conversationAfter the call endsCall data, transcript, recording URLs, agent variables
analytics-completedAfter post-call analytics finishSummary, disposition and success metrics, call data

All three events for a call share the same callId. Use it to join them.

How it fits together

  1. Endpoint: a URL that receives events. Each endpoint has its own signing secret, retention, and custom headers.
  2. Subscription: links an endpoint to an agent (or to all agents). It also chooses which events to send and which fields go into each payload.
  3. Delivery: one event sent to one endpoint. Each delivery has one or more attempts.

One endpoint can serve many agents, and one agent can send to many endpoints.


1. Open Webhooks

Location: Left sidebar → Configure → Webhooks

Webhooks page listing endpoints with status, 24-hour success rate, retention and created date
The Webhooks page: endpoints, organization-wide deliveries, and the audit log

The page has three tabs:

  • Endpoints: every endpoint with its health status, 24-hour success rate, retention, and created date. Click a row to open it.
  • Deliveries: every delivery across all endpoints. See Organization-wide deliveries.
  • Audit log: every webhook change made in your organization. See Audit log.

Endpoint status:

StatusMeaning
ActiveRecent deliveries are succeeding
DegradedSome recent attempts are failing
FailingMost or all recent attempts are failing
DisabledYou paused the endpoint. Nothing is sent.

2. Create an endpoint

Click New endpoint.

New webhook endpoint dialog with name, URL, description, payload retention and custom headers
New webhook endpoint
FieldRequiredNotes
NameYesUp to 120 characters. Shown everywhere in the dashboard.
Endpoint URLYesHTTPS only, up to 2048 characters. Private, loopback, and link-local addresses are rejected.
DescriptionNoUp to 2000 characters.
Payload retention (days)No1 to 365 days. Default is 30. How long payloads are kept for viewing, retry, and replay.
Custom headersNoUp to 10 headers sent with every request, for example an API-gateway key. Values are encrypted and never shown again after you save.

Custom header names can be up to 128 characters and values up to 1024. These names are reserved and can’t be used: content-type, content-length, user-agent, host, connection, transfer-encoding, webhook-id, webhook-timestamp, webhook-signature, x-signature.

Click Create endpoint. You can have up to 150 endpoints per organization.

Copy your signing secret

After you create an endpoint, the dashboard shows its signing secret (whsec_…) once. Copy it into your secret store right away. You won’t see it again. If you lose it, rotate the secret.

Then click Done, add a subscription to go straight to the Subscriptions tab.

3. Subscribe agents

An endpoint receives nothing until you subscribe at least one agent.

Endpoint detail page with health, Send test event, the five tabs and an Add agent button
Endpoint detail page: header, tabs, and Add agent

Open the endpoint, go to Subscriptions, and click Add agent.

Add agent picker with search, All agents option and a list of agents
Pick one agent, or All agents

Search for an agent, or choose All agents to cover every agent in the organization, including agents you create later.

Choose events and fields

Subscription sheet with a switch per event, Full payload or Choose fields, and field checkboxes
Per-agent subscription: events and payload fields

Each event has its own switch. For each enabled event, choose one of:

  • Full payload: every field for that event, including fields added in the future.
  • Choose fields: only the fields you tick.
EventSelectable fields
pre-conversationfromPhone, toPhone
post-conversationcallData, transcript, recordingUrl, recordingDualUrl, variables
analytics-completedanalytics, callData

These fields are always sent, whatever you choose: agentId, eventType, conversationType, callId, toolCallPayloadsOmitted.

Click Save. To unsubscribe, open the agent again and click Remove agent.

Leave out what you don’t use. For example, skip recordingUrl and recordingDualUrl if you only store transcripts. Smaller payloads are faster to deliver and expose less data.

Or: set it up from the agent

You can also connect a webhook from inside an agent.

Location: Agent → Tools → System triggers → Webhook

Agent Tools page showing the Webhook system trigger
Webhook system trigger on the agent's Tools page

Turn on the switch, or click the pencil, to open the webhook panel.

Agent webhook panel with endpoint select, event checkboxes, branches warning, Save and Remove
Pick an endpoint and the events to send
  1. Choose a Webhook endpoint. The endpoint must already exist under Webhooks.
  2. Tick the Events to subscribe: Start (pre-conversation), End (post-conversation), and Analytics completed.
  3. Click Save. Use Remove to unsubscribe the agent from that endpoint.

Webhook subscriptions apply immediately across all branches of the agent. They aren’t versioned with the agent.

4. Send a test event

Click Send test event on the endpoint page. This sends a signed sample payload to your endpoint and shows the result inline.

Send test event dialog with an event type selector
Send a test event
  • Event type: the default is Same as the first enabled subscription. You can also pick a specific event.
  • The dialog waits up to 60 seconds and then shows the HTTP status and response.
  • Test deliveries appear in Deliveries with the trigger TEST.

Use this to check your URL, custom headers, and signature verification before real calls arrive.


Deliveries

The Deliveries tab on an endpoint lists everything sent to it.

Deliveries table with status, event and time filters, call ID search and Replay deliveries
Endpoint deliveries with filters
  • Filters: status, event type, time range, and Call ID. Use Call ID to see every event for one call.
  • Attempts: how many times this delivery has been tried.
  • Replay deliveries: re-send many deliveries at once. See Replay.
  • Click any row to open its details.

Delivery statuses:

StatusMeaning
PendingQueued and not sent yet
RetryingAn attempt failed. Waiting for the next retry.
SendingAn attempt is in progress
SucceededYour endpoint returned 2xx
FailedAll attempts are used up, or it can’t be sent (see failure reasons below)

Triggers: EVENT (a real call), TEST (Send test event), MANUAL (Retry delivery), REPLAY (bulk replay).

Delivery details

Delivery detail with Retry delivery button, delivery facts and a timeline of attempts
One delivery: facts, then every attempt

The detail view shows:

  • The event, call ID, event ID, trigger, and created time.
  • Every attempt: when it ran, the outcome, HTTP status, response time, and the first part of your response body.
  • Retry delivery: sends this delivery again now. The event ID stays the same, so your deduplication still works.
Delivery payload JSON viewer
The exact JSON payload that was sent

You can view the payload until the endpoint’s retention period ends. After that, the payload is purged and can’t be retried or replayed.

Attempt outcomes: SUCCESS, HTTP_3XX, HTTP_4XX, HTTP_5XX, TIMEOUT, CONNECTION_ERROR, TLS_ERROR, BLOCKED_URL, BODY_TOO_LARGE, ENDPOINT_BUSY, LEASE_EXPIRED, UNKNOWN.

Failure reasons (why a delivery ended as Failed):

ReasonWhat to do
EXHAUSTEDAll 7 attempts failed. Fix your endpoint, then retry or replay.
ENDPOINT_DISABLEDThe endpoint was disabled. Enable it, then replay.
SUBSCRIPTION_DISABLEDThe agent’s subscription was removed or turned off
NO_ACTIVE_SECRETEvery signing secret is expired or revoked. Rotate to create a new one.
PAYLOAD_EXPIREDThe payload is past its retention period
BLOCKED_URLThe URL resolves to a private or disallowed address
BODY_TOO_LARGEThe payload was larger than 1 MB, even after trimming

Replay missed events

Use replay to recover after an outage. Click Replay deliveries.

Replay form with a time window and status selection
Choose what to replay
  1. Choose a time window. It can cover up to 31 days.
  2. Choose the statuses to include. The default is Failed. You can add Succeeded to re-send everything.
  3. Click Review.
Replay confirmation showing the number of matching deliveries and a Confirm button
Confirm the number of deliveries to replay

Check the count and click Confirm. Replays keep the same event ID as the original, and deliveries whose payload has already been purged are skipped.

Organization-wide deliveries

Webhooks → Deliveries shows deliveries for every endpoint. Filter by endpoint, or use the Endpoint column to see where each one went.

Organization-wide deliveries with endpoint filter and endpoint column
Deliveries across all endpoints

Performance

The Performance tab shows how your endpoint is doing.

Performance tab with window picker, stat cards, charts and a failure breakdown
Endpoint performance
  1. Window: 1h, 3h, 12h, 24h, 7d, or 30d.
  2. Success rate: the share of deliveries that succeeded.
  3. First-try success: the share that succeeded on the first attempt, with no retry.
  4. Response time (p95): how fast your endpoint responds.
  5. Failed attempts: the number of attempts that failed in the window.
  6. Charts: p95 latency and success rate over time.
  7. Why attempts failed: failed attempts grouped by outcome, for example HTTP_5XX or TIMEOUT.

Settings

Settings tab with Endpoint card, Edit button, signing secrets list, Rotate secret and Revoke
Endpoint settings and signing secrets

Edit the endpoint

Click Edit on the Endpoint card to change the name, URL, description, retention, or custom headers. Custom header values stay hidden. Type a new value to replace one.

Signing secrets

StatusMeaning
ActiveUsed to sign requests
ExpiringA previous secret that still signs requests until its expiry time
ExpiredNo longer used
RevokedYou turned it off manually

Rotate secret creates a new secret and shows it once.

  • Expire previous secret in (hours) sets how long the old secret keeps signing: 0 to 24 hours, default 24. During that time, every request carries one signature per active secret. This lets you deploy the new secret without dropping requests.
  • Set it to 0 if the old secret leaked. It stops being used right away.

Revoke turns off a secret immediately. You can’t revoke the last active secret. Rotate first.

Security & verification

Security and verification section with a Node.js snippet, plus the danger zone
How requests are protected, and a copy-ready verifier

This section summarizes how requests are signed and includes a Node.js verifier you can Copy. Full details are in Verify signatures.

Disable or delete

  • Disable endpoint pauses delivery. New events are recorded as failed with ENDPOINT_DISABLED. After you Enable the endpoint again, replay them.
  • Delete endpoint stops all delivery and removes its subscriptions. The endpoint’s history stays in the audit log.

Audit log

Every webhook change is recorded, both per endpoint (Audit log tab) and for the whole organization (Webhooks → Audit log).

Audit log with time, actor, action and an expandable before/after diff
Who changed what, and when

Each row shows When, Actor, Action, and IP. Expand Changes to see a before/after diff.

Recorded actions: endpoint created, updated, or deleted; subscription added, updated, or removed; secret rotated or revoked; test event sent; delivery retried; deliveries replayed.


Receiving webhooks

Request format

POST /your/path HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Smallest-Webhooks/1.0 (+https://docs.smallest.ai/webhooks)
Webhook-Id: 3f1c2a9e-... # event ID, the same on every retry
Webhook-Timestamp: 1761484800 # unix seconds
Webhook-Signature: t=1761484800,v1=5d2c... # one v1 per active secret
X-Signature: ... # legacy header, see below
X-Api-Key: ... # your custom headers, if any

The body is a JSON envelope:

{
"id": "3f1c2a9e-...",
"url": "https://example.com/hooks/atoms",
"description": "CRM sync",
"event": "69fc7dd...e2.post-conversation",
"metadata": {
"agentId": "...",
"eventType": "post-conversation",
"conversationType": "telephonyOutbound",
"callId": "CALL-...",
"toolCallPayloadsOmitted": false,
"callData": { "...": "..." },
"transcript": [ "..." ],
"variables": { "...": "..." }
}
}
FieldDescription
idEvent ID. Same as the Webhook-Id header and the same on every retry.
urlThe endpoint URL this delivery was sent to.
descriptionThe endpoint description, if set.
event<agentId>.<eventType>, for example 69fc7dd...e2.post-conversation.
metadata.agentIdAlways sent. The agent that handled the call.
metadata.eventTypeAlways sent. The event type, for example post-conversation.
metadata.conversationTypeAlways sent. One of telephonyInbound, telephonyOutbound, webcall, or chat.
metadata.callIdAlways sent. The call ID.
metadata.toolCallPayloadsOmittedAlways sent. true when tool-call payloads were dropped to fit the size limit.
Other metadata fieldsOnly the fields you selected on the subscription.

Verify signatures

Webhook-Signature has the form t=<timestamp>,v1=<hex>. Each v1 is an HMAC-SHA256 of "<timestamp>.<raw request body>", keyed with one of your active signing secrets. During a rotation there’s more than one v1. Accept the request if any of them matches.

Always:

  1. Verify against the raw body bytes, before any JSON parsing.
  2. Reject requests whose Webhook-Timestamp is more than 5 minutes old.
  3. Compare signatures in constant time.
  4. Deduplicate on Webhook-Id. Retries and replays reuse it.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (Buffer or string)
export function verifyWebhook(rawBody, headers, secret) {
const timestamp = headers["webhook-timestamp"];
const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!timestamp || ageSeconds > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return (headers["webhook-signature"] || "")
.split(",")
.filter((part) => part.startsWith("v1="))
.some((part) => {
const given = Buffer.from(part.slice(3));
return given.length === expected.length &&
crypto.timingSafeEqual(given, Buffer.from(expected));
});
}

Legacy X-Signature: for compatibility with existing integrations, requests also carry the older X-Signature header, signed with your oldest active secret. It will be turned off in a future release. Migrate to Webhook-Signature.

Respond quickly

  • Return any 2xx within 30 seconds. Do slow work asynchronously after you respond.
  • 3xx redirects are not followed and count as failures.
  • Any non-2xx, timeout, or connection error triggers a retry.

Retries

A delivery gets up to 7 attempts. After the first attempt fails, retries follow roughly this schedule (each delay varies by ±20%):

AttemptDelay after the previous attempt
230 seconds
32 minutes
410 minutes
51 hour
66 hours
724 hours

If every attempt fails, the delivery is marked Failed about 31 hours after the event. You can then retry it or replay it.

Payload size

Payloads are capped at 1 MB. If a payload is larger, tool-call arguments and responses in the transcript are dropped and toolCallPayloadsOmitted is set to true. If it’s still too large, the delivery fails with BODY_TOO_LARGE.


Limits

LimitValue
Endpoints per organization150
URL length2048 characters, HTTPS only
Name / description120 / 2000 characters
Custom headers10 per endpoint, name ≤ 128, value ≤ 1024 characters
Payload retention1–365 days (default 30)
Request timeout30 seconds
Attempts per delivery7
Payload size1 MB
Replay window31 days
Previous-secret overlap on rotation0–24 hours (default 24)

FAQ

Not yet. The new webhooks are managed from the dashboard only.

Possibly. Retries, manual retries, and replays all reuse the same Webhook-Id. Store the IDs you’ve processed and skip any you’ve already seen.

Not guaranteed. Events for a call are sent as they happen, but retries can reorder them. Use callId and eventType to put them back together.

No. Retries cover about 31 hours. If deliveries already failed, use Replay deliveries with status Failed for the time window of the outage.

Open Settings → Rotate secret. Set the previous-secret expiry to cover your deploy, or set it to 0 if the secret leaked.

For security, delivery goes only to public HTTPS endpoints. Use a tunnel such as ngrok for local testing.