Webhooks

Receive real-time events from Nerva when your agents complete calls, chats, or capture leads.

Webhooks let you receive real-time events from Nerva when your agents complete calls, chats, or capture leads. Instead of polling our API, configure a URL once and Nerva will POST events to it as they happen.

Setup

  1. Open Dashboard → Integrations.
  2. Click Webhook in the integrations catalog.
  3. Fill in:
    • Name — any label you'll recognize ("Production Zapier", "QA endpoint").
    • Webhook URL — the HTTPS endpoint that will receive events.
    • Secret (optional but recommended) — used to sign requests with HMAC-SHA256. Click Generate for a strong random secret.
    • Active — toggle off to pause delivery without deleting the config.
    • Scope — see Scope below.
    • Authentication — see Authentication below.
    • Events — pick which events should fire this webhook. See the Event reference.
  4. Save. Click Send test event to verify your endpoint is reachable.

You can edit, pause, or delete a webhook from the same page at any time.

Scope

Webhooks fire for either every agent in your organization or a specific subset:

  • All agents in this organization — fires on every event from every agent. Best for org-wide analytics or a single CRM endpoint.
  • Selected agents only — fires only for agents you explicitly attach. Attach from each agent's Integrations tab. Best when one Nerva organization hosts multiple end-customers and each routes to its own CRM endpoint.

Both kinds of webhooks fire independently and additively — if you have an org-wide webhook and a per-agent webhook that both subscribe to call.ended, each call ending fires both webhooks (two HTTP POSTs, two delivery log rows).

Authentication

Two layers, both independent of each other:

HMAC signature (always-on when a secret is configured)

When you set a secret, every request carries an X-Nerva-Signature header containing the hex SHA-256 HMAC of the raw request body. Verifying this proves the request came from Nerva and the body wasn't tampered with in transit. See Verifying webhook signatures for code samples.

Customer-controlled Authorization header

Independent of HMAC, you can configure one of:

MethodWhat we send
NoneNo Authorization header
Bearer tokenAuthorization: Bearer <your token>
Basic authAuthorization: Basic <base64(user:pass)>
API key (custom header)<your header name>: <your value> (e.g. X-API-Key)

This is what most no-code platforms (Zapier, Make, n8n) and internal API gateways expect. You can use both HMAC and a customer auth header at the same time — the security guarantees stack.

Reserved header names you cannot override (Nerva owns these): Content-Type, Content-Length, X-Nerva-Signature, X-Nerva-Timestamp.

Payload format

Every webhook is a POST with this envelope:

{
  "event": "call.ended",
  "timestamp": "2026-05-10T11:32:00.000Z",
  "organizationId": "org_xxxxx",
  "data": {
    /* event-specific fields — see /docs/webhook-events */
  }
}
  • event — the event ID. See the full Event reference.
  • timestamp — ISO 8601 UTC, set by Nerva at delivery time.
  • organizationId — your Nerva organization ID.
  • data — event-specific payload. All keys are camelCase.

Headers:

  • Content-Type: application/json
  • X-Nerva-Signature: <hex> (when secret is configured)
  • Authorization: ... (when configured)

Delivery semantics

  • HTTP method: POST with JSON body.
  • Timeout: 10 seconds per request. Endpoints that consistently take longer will be retried as failed.
  • Retries: failed deliveries retry with exponential backoff up to 6 attempts spread over ~24 hours. We retry on 5xx, 408, 429, network errors, and DNS failures.
  • No retry on 4xx (except 408 and 429). A 4xx means the endpoint is misconfigured (auth, route, payload format) and retrying won't fix it.
  • Concurrency cap: at most 5 concurrent in-flight deliveries per webhook. This prevents bursts of activity (e.g., a campaign ending 50 calls at once) from overwhelming your endpoint with 429s.
  • Idempotency: each delivery has a stable identifier (integrationId:event:resourceId). If Nerva's internals re-emit the same event, your endpoint will receive exactly one request per delivery.
  • Ordering: not guaranteed across events. call.ended and call.analyzed for the same call are emitted in order, but if *.ended retries while *.analyzed succeeds, you may receive *.analyzed before the eventual *.ended retry. Build idempotent handlers keyed on the resource ID.

Successful response: any 2xx status code. Body is ignored.

Best practices

  1. Respond fast — return 2xx immediately, then process asynchronously. A 10s timeout looks like a failure to us; long-running processing inside the request will trigger retries.
  2. Verify HMAC in production — always. See Verifying webhook signatures.
  3. Use HTTPS — webhooks contain transcripts and contact data. We do not restrict HTTP URLs at write time, but you should never use plain HTTP for real traffic.
  4. Use idempotency keys on your side too — events use callId (or sessionId for messaging) as a stable resource identifier. If your handler is invoked twice for the same callId + event, only act once.
  5. Subscribe to what you actually need*.started events are off by default; turn them on only if you need them. Fewer subscriptions = fewer webhook deliveries to debug.
  6. Pause, don't delete — toggle a webhook to Inactive when migrating endpoints. Inactive webhooks receive no events but stay in your list for re-activation, and any in-flight retries are dropped cleanly.

Debugging

  • Test endpoint: in the webhook drawer, click Send test event to fire a synthetic payload at your URL with the production HMAC + auth headers.
  • Recent deliveries: the webhook drawer shows the last 20 delivery attempts with status codes, retry counters, and inline error messages.
  • Per-call activity: open any call detail page to see the webhook deliveries for that specific call alongside CRM activity.

See also

On this page