Skip to main content

What Are Webhooks?

Webhooks are HTTP callbacks that TopCalls sends to your server when a call finishes. They’re the best way to integrate TopCalls with your existing systems.

Real-Time Updates

Get notified when calls complete or fail. No polling required.

Complete Data

Receive full call details, transcripts, recordings, and analysis in a single webhook payload.

Reliable Delivery

Built-in retry logic ensures webhooks are delivered even if your server is temporarily unavailable.

Filterable

Subscribe to specific dispositions or conversions so your endpoint only receives the events it cares about.

Setting Up Webhooks

There are two ways to receive webhooks.

Per-Call Webhooks

Set webhook_url when creating a call. That URL receives one payload when the call completes, fails, or is cancelled:
{
  "phone_number": "+14155551234",
  "task": "Confirm appointment...",
  "webhook_url": "https://your-app.com/webhooks/call-complete",
  "webhook_call_status_filter": ["completed", "failed"]
}
webhook_call_status_filter is optional. When set, only calls whose final status matches a listed value fire the webhook; leave it out (or send an empty array) to receive every outcome. It applies only to the per-call webhook_url, not to account-level subscriptions.

Account-Level Subscriptions

Subscribe a URL once and receive events for every matching call on your account, no matter how the call was created. Manage subscriptions through the API:
curl -X POST https://api.topcalls.ai/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "call.completed",
    "url": "https://hooks.your-app.com/topcalls"
  }'
Subscribe to a specific disposition by adding its slug as a suffix. For example, if your campaign’s post-call analysis labels a call Booked Callback, the event name is call.completed.booked_callback:
{
  "event": "call.completed.booked_callback",
  "url": "https://hooks.your-app.com/callbacks"
}
Narrow further with filters. Values match by equality, arrays match any element, strings with % match SQL-LIKE, and the special key is_conversion matches calls whose disposition is in the campaign’s conversion outcomes:
{
  "event": "call.completed",
  "url": "https://hooks.your-app.com/conversions",
  "filters": {
    "is_conversion": true,
    "phone_number": "+1555%"
  }
}
Use GET /v1/webhooks to list subscriptions and DELETE /v1/webhooks/{webhook_id} to remove one. See the API reference for full schemas.

Campaign Webhooks

Campaigns configure their webhook URL in the TopCalls dashboard. The campaign editor also lets you pick which call statuses fire the webhook (for example only answered calls, or only voicemail), which keeps noise out of your downstream tools.

Webhook Payload

Here’s a complete call.completed webhook payload:
{
  "call_id": "564d4fd4-03bc-400a-abe0-05540fbeff88",
  "phone_number": "+14155551234",
  "from_phone_number": "+18005551234",
  "status": "completed",
  "call_status": "completed",
  "duration": 2.5,
  "transcript": [
    {
      "id": 1,
      "user": "assistant",
      "text": "Hi, this is Rachel from TopView Dental...",
      "created_at": "2025-12-22T10:30:05Z"
    },
    {
      "id": 2,
      "user": "user",
      "text": "Yes, hi...",
      "created_at": "2025-12-22T10:30:08Z"
    }
  ],
  "recording_url": "https://api.topcalls.ai/recordings/.../2025/12/22/mp3",
  "call_summary": "Successfully confirmed appointment...",
  "error_message": null,
  "answered_by": "human",
  "campaign_id": "camp_abc123",
  "lead_id": "lead_xyz789",
  "created_at": "2025-12-22T10:30:00Z",
  "started_at": "2025-12-22T10:30:05Z",
  "end_at": "2025-12-22T10:32:35Z",
  "disposition": "Appointment Booked",
  "is_conversion": true,
  "analysis": {
    "call_outcome": "Appointment Booked",
    "main_objection": null
  },
  "metadata": {
    "patient_id": "pat_123",
    "source": "reminder_system"
  }
}
Each transcript entry’s user field identifies the speaker: "assistant" for AI turns, "user" for caller turns. The field name is awkward but matches what the API actually sends. disposition is the label assigned by post-call analysis, and is_conversion is true when that disposition is in the campaign’s conversion outcomes list.

Webhook Timing

Successful Calls

The completion webhook fires shortly after the call ends, once the transcript and (if configured) the call summary are ready. The recording_url may be missing from this payload. Recordings take a few seconds longer to finish encoding, so a background job resolves the recording and writes it to the call record just after the webhook is sent. If you need the recording and it was not in the webhook, read the call back with GET /v1/calls/{call_id} and use the recording_url from that response.

Failed Calls

Webhooks are sent immediately when the call fails (no recording to wait for).

Handling Webhooks

Express.js Example

app.post('/webhooks/call-complete', async (req, res) => {
  // Always return 200 quickly
  res.status(200).json({ received: true });
  
  // Process asynchronously
  const call = req.body;
  
  // Update CRM
  await updateCRM(call);
  
  // Send notification
  await sendNotification(call);
  
  // Update analytics
  await updateAnalytics(call);
});

Securing Your Endpoint

Webhook payloads are not signed today, so treat the URL itself as the credential:
  • Serve the endpoint over HTTPS only.
  • Put an unguessable token in the path or query string (for example https://your-app.com/webhooks/tc_8f3k2m9x) and reject requests without it.
  • Before acting on a payload, you can confirm it against the API: fetch GET /v1/calls/{call_id} and compare.

Webhook Retries

TopCalls automatically retries failed webhook deliveries:
  • Initial: Immediate
  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 15 minutes
  • Retry 4: After 30 minutes
  • Retry 5: After 1 hour
After 5 retries, the webhook is marked as failed. You can still retrieve call data via the API.

Best Practices

✅ Do This

  • Return 200 quickly: Acknowledge receipt within 1 second
  • Process asynchronously: Don’t block the webhook response
  • Idempotency: Handle duplicate webhooks gracefully
  • Log everything: Keep logs for debugging
  • Keep the URL secret: Use an unguessable path token and HTTPS
  • Handle errors: Don’t let webhook processing crash your server

❌ Don’t Do This

  • Long processing: Don’t process synchronously in the webhook handler
  • Ignore duplicates: Webhooks may be delivered multiple times
  • Expose the URL: Don’t publish or log your webhook URL where others can see it
  • Block on external APIs: Don’t wait for slow external services

Testing Webhooks

Use ngrok or webhook.site to test locally:
# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL
{
  "webhook_url": "https://abc123.ngrok.io/webhooks/call-complete"
}

Next Steps

Webhooks & Events

Learn about available webhook events and when they fire.

API Reference

See complete webhook payload schemas in the API reference.