Webhooks
Aila sends webhook events to HTTP endpoints you register so you can react to call activity in real time — in Zapier, Make, n8n, your own backend, or any tool that accepts an HTTP POST.
Typical use cases:
- Push extracted call data into a spreadsheet or warehouse
- Create tasks in a CRM or project tracker when Aila auto-generates them
- Alert a channel when a mortgage call's MISMO export is ready to pull
- Trigger a custom workflow when a call finishes processing
Event types
| Event | Fires when | Payload highlights |
|---|---|---|
call.completed | A call finishes processing in Aila | callId, organizationId, recordedAt, durationSeconds, contactId |
call.data.extracted | Structured data has been pulled from the transcript | callId, full extracted data JSON (industry-specific groups + attributes) |
task.created | Aila auto-creates a task from a call based on one of your task triggers | taskId, callId, triggerId, triggerName, title, body, dueDate, confidence, matchedText, status |
task.synced | The auto-created task has been pushed to a connected CRM | taskId, callId, destination (gohighlevel / bonzo / ...), destinationTaskId, destinationUrl |
mismo.ready | A mortgage-lending MISMO 3.4 XML file has been generated | callId, format: "MISMO_3.4", xml (the full MISMO XML), warnings |
test.ping | You click Send test event in settings, or call the test endpoint | endpointId, timestamp, message |
Payloads never include the full transcript. If you need the transcript, respond to call.completed and fetch it with:
GET /api/v1/organizations/{organizationId}/calls/{callId}
Authorization: Bearer YOUR_API_KEYThe same pattern works for MISMO — even without subscribing to mismo.ready, any consumer of call.completed can pull the XML on demand:
GET /api/v1/organizations/{organizationId}/calls/{callId}/export/fnma
Authorization: Bearer YOUR_API_KEYDelivery format
Every delivery is an HTTP POST with JSON body. Headers include the event name, a unique delivery ID (idempotency key), the endpoint ID, and an HMAC signature.
POST https://your-endpoint.example.com/webhook
Content-Type: application/json
User-Agent: Aila-Webhooks/1.0
X-Aila-Event: call.completed
X-Aila-Delivery-Id: ai_webhook_delivery_abc123...
X-Aila-Webhook-Endpoint-Id: ai_webhook_endpoint_xyz789...
X-Aila-Signature: t=1713571200000,v1=9c4e0b...
{
"id": "ai_webhook_delivery_abc123...",
"type": "call.completed",
"createdAt": "2026-04-20T18:00:00.000Z",
"organizationId": "ai_organization_...",
"data": {
"callId": "ai_call_...",
"organizationId": "ai_organization_...",
"recordedAt": "2026-04-20T17:58:00.000Z",
"durationSeconds": 612,
"contactId": "ai_contact_..."
}
}Retries and failure handling
- Temporal-backed retry: up to 5 attempts with exponential backoff (30s, 2m, 10m, 1h, 6h).
- After 20 consecutive failed events the endpoint is automatically disabled. Re-enable it in settings once you've fixed the receiver.
X-Aila-Delivery-Idis stable across retries — use it to dedupe on your side.
Verifying signatures
Every delivery is signed with HMAC-SHA256 using your endpoint's secret. The X-Aila-Signature header looks like:
X-Aila-Signature: t=1713571200000,v1=9c4e0b...Where:
tis the unix timestamp (milliseconds) at which the signature was computedv1is the hex-encoded HMAC of{timestamp}.{raw_body}using your endpoint's secret
Recommended verification steps:
- Parse
tandv1from the header. - Reject requests older than 5 minutes (mitigates replay).
- Compute
HMAC_SHA256(secret, "{t}.{raw_body_as_string}"). - Compare the hex digest to
v1with a constant-time comparison.
Node.js example
import crypto from 'crypto'
function verifyAilaSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map(kv => kv.split('=')))
const timestampMs = Number(parts.t)
const provided = parts.v1
if (!Number.isFinite(timestampMs) || !provided) return false
const ageSeconds = Math.abs(Date.now() - timestampMs) / 1000
if (ageSeconds > toleranceSeconds) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestampMs}.${rawBody}`)
.digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(provided, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}Python example
import hmac, hashlib, time
def verify_aila_signature(raw_body: bytes, header: str, secret: str, tolerance_s: int = 300) -> bool:
parts = dict(kv.split('=') for kv in header.split(','))
timestamp_ms = int(parts['t'])
provided = parts['v1']
age_s = abs(time.time() * 1000 - timestamp_ms) / 1000
if age_s > tolerance_s:
return False
expected = hmac.new(
secret.encode(),
f"{timestamp_ms}.{raw_body.decode()}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, provided)Managing webhooks via API
All webhook management endpoints live under:
/api/v1/organizations/{organizationId}/webhooksand require a Bearer API key. See Authentication.
List webhooks
GET /api/v1/organizations/{organizationId}/webhooksResponse:
{
"success": true,
"data": {
"webhooks": [
{
"id": "ai_webhook_endpoint_...",
"name": "Zapier — Tasks to Sheet",
"url": "https://hooks.zapier.com/hooks/catch/...",
"eventTypes": ["task.created", "task.synced"],
"active": true,
"source": "manual",
"consecutiveFailureCount": 0,
"lastDeliveryAt": "2026-04-20T18:00:00.000Z",
"lastSuccessAt": "2026-04-20T18:00:00.000Z",
"createdAt": "2026-04-18T10:00:00.000Z",
"updatedAt": "2026-04-20T18:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
}Create a webhook
POST /api/v1/organizations/{organizationId}/webhooks
Content-Type: application/jsonBody:
{
"name": "Zapier — Tasks to Sheet",
"url": "https://hooks.zapier.com/hooks/catch/12345/abcdef/",
"eventTypes": ["task.created", "task.synced"],
"active": true
}Response includes the generated secret — this is shown only once. Store it securely for signature verification.
{
"success": true,
"data": {
"webhook": {
"id": "ai_webhook_endpoint_...",
"name": "Zapier — Tasks to Sheet",
"url": "https://hooks.zapier.com/hooks/catch/12345/abcdef/",
"eventTypes": ["task.created", "task.synced"],
"active": true,
"source": "manual",
"secret": "8f4c3a2e7b9d...",
"createdAt": "2026-04-20T18:00:00.000Z"
}
}
}Get / update / delete a single webhook
GET /api/v1/organizations/{organizationId}/webhooks/{webhookId}
PATCH /api/v1/organizations/{organizationId}/webhooks/{webhookId}
DELETE /api/v1/organizations/{organizationId}/webhooks/{webhookId}PATCH accepts any subset of name, url, eventTypes, active. Setting active: true also resets consecutiveFailureCount — useful when re-enabling an auto-disabled endpoint.
DELETE soft-deletes the endpoint; past deliveries remain in the audit trail.
List recent deliveries
GET /api/v1/organizations/{organizationId}/webhooks/{webhookId}/deliveries?limit=100Returns the most recent delivery attempts (up to 250) with status, HTTP response code, attempt count, and error message.
Send a test event
POST /api/v1/organizations/{organizationId}/webhooks/{webhookId}/testFires a test.ping payload to the endpoint even if it isn't subscribed to test.ping. Use this to verify the URL is reachable and your HMAC check is correct.
Rotate the signing secret
POST /api/v1/organizations/{organizationId}/webhooks/{webhookId}/rotate-secretGenerates a new secret and returns it once. Update your receiver to use the new secret before firing the next event.
Fetching call data after a webhook
Webhook payloads are intentionally compact. Once you receive a call.completed event, use the call ID to pull the full record:
const res = await fetch(
`https://api.aila.com/v1/organizations/${organizationId}/calls/${callId}`,
{ headers: { Authorization: `Bearer ${process.env.AILA_API_KEY}` } }
)
const { data } = await res.json()
// data.transcript, data.data (extracted fields), data.metadata, etc.For MISMO 3.4 XML (mortgage-lending only):
const res = await fetch(
`https://api.aila.com/v1/organizations/${organizationId}/calls/${callId}/export/fnma`,
{ headers: { Authorization: `Bearer ${process.env.AILA_API_KEY}` } }
)
const xml = await res.text()Zapier quickstart
- In Zapier, create a new Zap with the Webhooks by Zapier → Catch Hook trigger. Copy the hook URL.
- In the Aila API (or the settings UI once available), create a webhook endpoint pointing at that URL and subscribe to the events you care about.
- Click Send test event — Zapier should receive it and let you pick sample fields.
- Add downstream Zap steps (e.g., Google Sheets, Slack, Airtable) mapping fields from the
dataobject.
For actions that need to fetch additional data (full transcript, MISMO XML), use Zapier's Webhooks by Zapier → GET action with your Aila API key in the Authorization header.