Skip to main content
🤖 LLM Friendly: This page is available in raw Markdown format for LLM consumption:overview.md|Get full documentation:llms.txt/llms-full.txt

Webhooks Overview

Webhooks allow your application to receive real-time HTTP notifications when events occur in your Firetell workspace.

How Webhooks Work

  1. You register a webhook URL via the Webhooks API or the Firetell Console
  2. When an event occurs, Firetell sends an HTTP POST request to your URL
  3. Your server processes the event and responds with a 200 status code

Webhook Payload Format

All webhook payloads follow a standard envelope structure:

{
"event": "call.ended",
"data": {
"workspace_id": "yourcompany",
"occurred_at": "2026-07-24T00:56:02.000Z",
"attempt": 1,
"call_id": "call_abc789",
"direction": "inbound",
"status": "completed",
"type": "audio",
"from": {
"name": "+84901234567",
"number": "+84901234567"
},
"to": {
"name": "+84909876543",
"number": "+84909876543"
},
"started_at": "2026-07-24T00:55:00.000Z",
"answered_at": "2026-07-24T00:55:02.000Z",
"ended_at": "2026-07-24T00:56:02.000Z",
"duration": 62,
"billsec": 60,
"cost": 0.0517,
"hangup_cause": "NORMAL_CLEARING"
}
}

Event Payload Examples

call.created

{
"event": "call.created",
"data": {
"workspace_id": "yourcompany",
"occurred_at": "2026-07-24T00:55:00.000Z",
"attempt": 1,
"call_id": "call_abc789",
"direction": "inbound",
"status": "started",
"type": "audio",
"from": { "name": "+84901234567", "number": "+84901234567" },
"to": { "name": "+84909876543", "number": "+84909876543" },
"started_at": "2026-07-24T00:55:00.000Z",
"answered_at": null,
"ended_at": null,
"duration": 0,
"billsec": 0,
"cost": 0,
"hangup_cause": null
}
}

call.recording.ready

{
"event": "call.recording.ready",
"data": {
"workspace_id": "yourcompany",
"occurred_at": "2026-07-24T00:56:05.000Z",
"attempt": 1,
"recording_id": "rec_call_abc789",
"call_id": "call_abc789",
"url": "https://storage.firetell.app/workspaces/yourcompany/recordings/call_abc789.mp3",
"duration": 60,
"size_bytes": 1440000,
"format": "mp3",
"created_at": "2026-07-24T00:56:05.000Z"
}
}

call.transcription.completed

{
"event": "call.transcription.completed",
"data": {
"workspace_id": "yourcompany",
"occurred_at": "2026-07-24T00:56:06.000Z",
"attempt": 1,
"call_id": "call_abc789",
"dialogues": [
{
"id": "diag_01h900000000000000000001",
"speaker": "caller",
"channel_index": 0,
"text": "Hello, I would like to check my order status for #FT10294.",
"confidence": 0.98,
"duration_ms": 3200,
"timestamp": "2026-07-24T00:55:10.000Z"
},
{
"id": "diag_01h900000000000000000002",
"speaker": "agent",
"channel_index": 1,
"text": "Certainly! I have retrieved order #FT10294 for Alex Rivera. It is scheduled for delivery today with Express Courier.",
"confidence": 0.99,
"duration_ms": 5400,
"timestamp": "2026-07-24T00:55:20.000Z"
}
],
"entities": [
{
"type": "order_id",
"value": "#FT10294",
"confidence": 0.99
},
{
"type": "customer_name",
"value": "Alex Rivera",
"confidence": 0.98
},
{
"type": "carrier",
"value": "Express Courier",
"confidence": 0.97
}
],
"total_dialogues": 2,
"completed_at": "2026-07-24T00:56:06.000Z"
}
}

Event Types

EventDescription
call.createdA new call has been initiated
call.answeredA call was answered by an agent or user
call.endedA call has ended
call.recording.readyA call recording audio file has finished processing, uploaded, and is ready for playback/download
call.transcription.completedReal-time call transcription and Named Entity Recognition (NER) completed
contact.createdA new contact was created in the workspace
contact.updatedAn existing contact was updated
contact.deletedA contact was deleted
agent.stateAn agent's presence state was changed (available, busy, away, offline)
agent.createdA new workspace agent account was created
agent.updatedAn existing workspace agent account was updated
agent.deletedA workspace agent account was deleted
workspace.credit.updatedWorkspace credit balance was updated (e.g. call charge deduction, top-up)

Retry Policy

If your endpoint returns a non-2xx status code or times out (30 seconds), Firetell will retry the delivery:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours

After 4 failed retries, the webhook is marked as failed and no further attempts are made.

Security

Validate incoming webhooks by checking the X-Webhook-Signature header. This header contains an HMAC-SHA256 signature of the JSON request body, signed with your webhook secret (prefixed with whse-).

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

// Usage in an Express handler:
app.post('/webhooks/firetell', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const isValid = verifySignature(
JSON.stringify(req.body),
signature,
'whse-your-webhook-secret'
);

if (!isValid) return res.status(401).send('Invalid signature');

// Process event...
res.status(200).send('OK');
});

Best Practices

  • Respond quickly — Return 200 immediately and process asynchronously
  • Handle duplicates — Use the event ID for idempotency
  • Use HTTPS — Always use HTTPS endpoints in production
  • Validate signatures — Verify the X-Webhook-Signature header