Webhooks
Aila sends signed HTTPS POST requests to your endpoint when calls finish processing, data is extracted, tasks and appointments are created, or a MISMO 3.4 file is ready.
Events
| Category | Event | Sent when |
|---|---|---|
| Calls | call.completed | A call finishes processing. The payload is lightweight (IDs, time, duration, contact); fetch details with the API. |
| Calls | call.data.extracted | Structured data from your Call setup is ready. |
| Tasks | task.created | Aila creates a follow-up task from a call. |
| Tasks | task.synced | A task syncs to your CRM. |
| Appointments | appointment.created | Aila creates an appointment from a call. |
| Appointments | appointment.synced | An appointment syncs to your CRM calendar. |
| Loan files | mismo.ready | A MISMO 3.4 file is generated for a call or contact. |
| Testing | test.ping | Only when you click Send test event. |
call.completed and call.data.extracted are sent once per call.
Add an endpoint
In the app
- Go to Settings > Connections and open Outgoing webhooks.
- Click New webhook.
- Enter a Name and the URL. The URL must be HTTPS and publicly reachable (no localhost or private IPs).
- Select the events to send.
- Save, then copy the signing secret. It's shown only once.
- Click Send test event to check your endpoint.
Each endpoint's Recent deliveries shows the event, status, number of attempts, and time.
With the API
curl -X POST https://app.aila.fyi/api/v1/organizations/{organizationId}/webhooks \
-H "Authorization: Bearer ak_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Internal CRM ingest",
"url": "https://api.yourdomain.com/webhooks/aila",
"eventTypes": ["call.completed", "call.data.extracted"]
}'The response includes the signing secret. The API also supports listing, updating, deleting, sending a test event, rotating the secret, and listing deliveries. See the OpenAPI spec (opens in a new tab).
Payload
{
"id": "evt_...",
"type": "call.completed",
"createdAt": "2026-09-30T14:30:00.000Z",
"organizationId": "org_...",
"data": {
"callId": "...",
"organizationId": "org_...",
"recordedAt": "2026-09-30T14:02:11.000Z",
"durationSeconds": 642,
"contactId": "...",
"contactName": "Dana Brooks",
"contactPhone": "+19195550123"
}
}Headers
| Header | Value |
|---|---|
X-Aila-Event | The event type, for example call.completed. |
X-Aila-Delivery-Id | Unique per delivery. Use it to ignore duplicates. |
X-Aila-Webhook-Endpoint-Id | The endpoint that received it. |
X-Aila-Signature | t=<timestamp_ms>,v1=<hex HMAC> |
Verify the signature
The signature is an HMAC-SHA256 of <timestamp>.<raw body> using your signing secret. Reject requests older than 5 minutes.
import crypto from 'crypto'
function verifyAilaWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(signatureHeader.split(',').map((kv) => kv.split('=')))
const timestamp = Number(parts.t)
if (!timestamp || !parts.v1) return false
if (Math.abs(Date.now() - timestamp) / 1000 > toleranceSeconds) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(parts.v1, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}Use the raw request body exactly as received, before parsing JSON.
To rotate the secret, click Rotate signing secret on the endpoint. The old secret stops working immediately.
Delivery and retries
- Success: any
2xxresponse within 15 seconds. Redirects count as failures. - Retries: up to 5 attempts in total, with exponential backoff starting at 30 seconds (up to 1 hour between attempts, 7 hours overall).
- Auto-disable: an endpoint is disabled after 20 consecutive failed deliveries. Fix the endpoint, then re-enable it in settings.