Separate inbound and outbound webhook flows clearly.
Callaro integrations can involve two webhook directions:
- Webhooks sent by Callaro to your systems (for example post-call notifications).
- Webhooks received by Callaro from providers/integrations (for example billing or telephony callbacks).
This page documents consumer guidance for webhooks sent by Callaro and links receiver-side endpoints separately.
Webhooks sent by Callaro (to your endpoint)
Event families in active use
call.completed (post-call operational payload)
- campaign lifecycle events where configured per tenant integration policy
- compliance and number-health events where enabled
Canonical call.completed payload shape
{
"event": "call.completed",
"call_id": "vs_12345",
"voice_session_id": 9001,
"call_sid": "CA123...",
"tenant_id": 44,
"agent_id": 42,
"direction": "outbound",
"from_e164": "+14155550100",
"to_e164": "+919900000001",
"extracted_data": {},
"runtime_vars": {},
"tool_calls": []
}
Delivery contract (current public status)
The current public webhook contract does not guarantee a signature header. Do not build hard dependency on X-Callaro-Signature unless your tenant has a separately documented private contract.
- Delivery model: at least once
- Dedupe key:
call_id (and optionally voice_session_id)
- Ordering: not guaranteed across calls
- Success response expected: any 2xx status
- Retry behavior: automatic retries for transient failures/non-2xx
Receiver implementation template
curl -X POST "https://your-app.example.com/callaro/webhooks" \
-H "Content-Type: application/json" \
-d @payload.json
import express from 'express';
const app = express();
app.use(express.json({ limit: '2mb' }));
app.post('/callaro/webhooks', async (req, res) => {
const event = req.body;
const dedupeKey = `${event.call_id}:${event.event}`;
// Persist before side effects to make retries safe.
const alreadyProcessed = await hasProcessed(dedupeKey);
if (alreadyProcessed) return res.status(200).json({ ok: true, duplicate: true });
await storeRawWebhook(event);
await processWebhook(event);
await markProcessed(dedupeKey);
return res.status(200).json({ ok: true });
});
function hasProcessed(_dedupeKey) {
return Promise.resolve(false);
}
function storeRawWebhook(_event) {
return Promise.resolve();
}
function processWebhook(_event) {
return Promise.resolve();
}
function markProcessed(_dedupeKey) {
return Promise.resolve();
}
Webhooks received by Callaro (from external systems)
These are callback endpoints on Callaro that providers/integrations call. Typical examples include:
- telephony provider callbacks
- billing/payment status callbacks
- integration-specific event relays
Common telephony ingress endpoints:
POST /api/v1/webhooks/telephony/{vendor_slug}
POST /api/v1/webhooks/telephony/{vendor_slug}/sample
Treat these as separate contracts from outbound webhooks to your app. Their auth and payload verification rules are endpoint-specific and documented on those endpoint pages.
Reliability checklist
Persist raw payloads with receipt timestamp
Keep an audit log to support replay and incident analysis.
Deduplicate before side effects
Upsert by call_id and event type to prevent duplicate CRM writes.
Make downstream writes idempotent
Use external IDs and conditional updates in CRM/warehouse sinks.
Set explicit processing SLOs
Alert on backlog growth, non-2xx response rates, and replay volume.