Skip to main content

Webhooks

Webhooks let you receive real-time HTTP POST notifications when events occur in your project. Use them to sync call data with your CRM, trigger automations, log conversations, or build custom dashboards.

How It Works

  1. You register a webhook URL in Settings > Webhooks
  2. When an event occurs (e.g. a call ends), Callem sends an HTTP POST to your URL with the event payload
  3. Your server processes the payload and returns a 2xx status code
  4. If delivery fails, Callem retries up to 3 times with exponential backoff

Events

EventTrigger
call_startedA voice call begins (agent picks up)
call_endedA voice call ends (includes transcription and duration)
call_analyzedPost-call analysis completes (includes call analysis results)
chat_startedA chat session begins
chat_endedA chat session ends (includes transcription)
chat_analyzedPost-chat analysis completes (includes analysis results)

Creating a Webhook

1

Navigate to Settings

Go to Settings from the sidebar, then select the Webhooks tab.
2

Click Add Webhook

Click the Add Webhook button.
3

Configure the webhook

Fill in the following fields:
FieldDescriptionRequired
URLThe HTTPS endpoint that will receive eventsYes
DescriptionA label to identify this webhookNo
EventsWhich events to subscribe toYes (at least one)
Agent scopeAll agents or specific agents onlyYes
4

Save

Click Create. A signing secret is generated automatically.
Only HTTPS URLs are accepted. HTTP, localhost, and private IP addresses are blocked for security.

Agent Scope

By default, a webhook receives events for all agents in the project. You can restrict it to specific agents if you only want notifications for certain use cases (e.g. only your production sales agent).

Payload Structure

Every webhook delivery is an HTTP POST with a JSON body. The top-level structure is:
{
  "event": "call_ended",
  "timestamp": "2026-03-17T21:58:39.687Z",
  "call": { ... }
}

Headers

Each request includes the following headers:
HeaderDescription
Content-Typeapplication/json
x-callem-eventThe event name (e.g. call_ended)
x-callem-timestampUnix timestamp of the event
x-callem-signatureHMAC-SHA256 signature for verification
User-AgentCallem-Webhook/1.0

call_started

{
  "event": "call_started",
  "timestamp": "2026-03-17T21:57:57.654Z",
  "call": {
    "id": "69b9ce653d9cf893b843b8a7",
    "agentCollectionId": "6970f630646ae50d690c3035",
    "agentVersionNumber": null,
    "callerNum": "+33612345678",
    "calledNum": "+33187654321",
    "isLive": true,
    "createdAt": "2026-03-17T21:57:57.676Z"
  }
}

call_ended

Includes the full transcription (cleaned — only speaker messages and tool calls, no internal metadata):
{
  "event": "call_ended",
  "timestamp": "2026-03-17T21:58:39.687Z",
  "call": {
    "id": "69b9ce653d9cf893b843b8a7",
    "agentCollectionId": "6970f630646ae50d690c3035",
    "agentVersionNumber": 3,
    "callerNum": "+33612345678",
    "calledNum": "+33187654321",
    "duration": 39.812,
    "isEngaged": true,
    "totalInteraction": 2,
    "isLive": false,
    "transcription": [
      {
        "speaker": "Agent",
        "text": "Bonjour, comment puis-je vous aider ?",
        "startTime": 0.325
      },
      {
        "speaker": "User",
        "text": "Je voudrais connaître les délais de livraison.",
        "startTime": 5.1
      },
      {
        "speaker": "Tool",
        "data": { "toolName": "Retrieve" },
        "startTime": 15.2
      },
      {
        "speaker": "Agent",
        "text": "La livraison prend 24 à 48 heures.",
        "startTime": 16.6
      }
    ],
    "createdAt": "2026-03-17T21:57:57.676Z",
    "updatedAt": "2026-03-17T21:58:39.771Z"
  }
}

call_analyzed

Same as call_ended, plus the callAnalysis object containing the extracted fields:
{
  "event": "call_analyzed",
  "timestamp": "2026-03-17T21:58:45.123Z",
  "call": {
    "id": "69b9ce653d9cf893b843b8a7",
    "agentCollectionId": "6970f630646ae50d690c3035",
    "duration": 39.812,
    "transcription": [ ... ],
    "callAnalysis": {
      "primary_intention": "FAQ",
      "sentiment": "neutral",
      "is_automated": true,
      "call_outcome": "AUTOMATED",
      "faq_topic": "delivery",
      "faq_response_delivered": true
    },
    "createdAt": "2026-03-17T21:57:57.676Z",
    "updatedAt": "2026-03-17T21:58:39.771Z"
  }
}

Chat Events

Chat events (chat_started, chat_ended, chat_analyzed) follow the same structure but use a chat key instead of call, and do not include callerNum, calledNum, or duration.

Security — Verifying Signatures

Every webhook delivery is signed with your webhook’s secret using HMAC-SHA256. You should verify the signature to ensure the request is genuinely from Callem.

Verification Algorithm

  1. Read the x-callem-timestamp and x-callem-signature headers
  2. Construct the signed payload: {timestamp}.{request_body}
  3. Compute HMAC-SHA256 of that string using your webhook secret
  4. Compare the computed signature with x-callem-signature
Node.js Verification Example
const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const timestamp = req.headers['x-callem-timestamp'];
  const signature = req.headers['x-callem-signature'];
  const body = JSON.stringify(req.body);

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${body}`)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}
Always use a constant-time comparison (timingSafeEqual) to prevent timing attacks. Never compare signatures with ===.

Rotating the Secret

If you suspect your secret has been compromised:
  1. Open the webhook in Settings > Webhooks
  2. Click the menu icon and select Rotate Secret
  3. A new secret is generated immediately — the old secret stops working
  4. Update your server with the new secret
Rotating the secret is immediate. Any in-flight deliveries signed with the old secret will fail verification on your end. Update your server quickly after rotating.

Managing Webhooks

Editing

Click the Edit option in the webhook’s dropdown menu. You can change the URL, description, subscribed events, and agent scope. The signing secret is not affected.

Duplicating

Click Duplicate to create a copy of a webhook with the same configuration but a new signing secret. Useful for setting up staging and production webhooks with the same events.

Enabling / Disabling

Use the toggle switch on the webhook card to enable or disable it without deleting the configuration.

Deleting

Click Delete from the dropdown menu. A confirmation dialog appears before deletion.

Delivery Logs

Each webhook tracks recent delivery attempts. To view them:
  1. Open the webhook in Settings > Webhooks
  2. Click View Logs in the dropdown menu
  3. See the status (success/failed), HTTP status code, and timestamp for each delivery
Delivery logs are retained for 30 days.

Testing

To verify your webhook endpoint is working:
  1. Open the webhook in Settings > Webhooks
  2. Click Send Test from the dropdown menu
  3. A test call_started event is sent to your URL
  4. Check the delivery logs to confirm it was received
Test events are rate-limited to 5 per minute to prevent abuse.

Retry Policy

If your endpoint returns a non-2xx status code or the request times out (10 seconds), Callem retries delivery:
AttemptDelay
1st retry~2 seconds
2nd retry~4 seconds
3rd retry~8 seconds
After 3 failed attempts, the delivery is marked as failed in the logs. No further retries are attempted.

Limits

LimitValue
Max webhooks per project10
URL protocolHTTPS only
Request timeout10 seconds
Max retries3
Delivery log retention30 days
Test event rate limit5 per minute

Best Practices

Never trust a webhook payload without verifying the HMAC signature. This prevents malicious actors from sending fake events to your endpoint.
Process webhook payloads asynchronously. Return a 200 OK immediately and queue the actual processing. If your endpoint takes too long, the request may time out and trigger unnecessary retries.
In rare cases, the same event may be delivered more than once. Use the call.id or chat.id as an idempotency key to avoid processing duplicates.
If you only need events for specific agents (e.g. your production sales agent), configure the webhook’s agent scope to filter out irrelevant events.
Check the delivery logs periodically to ensure your endpoint is healthy. Persistent failures may indicate a misconfigured URL, firewall issue, or server error.