API Reference
Webhooks

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

EventFires whenPayload highlights
call.completedA call finishes processing in AilacallId, organizationId, recordedAt, durationSeconds, contactId
call.data.extractedStructured data has been pulled from the transcriptcallId, full extracted data JSON (industry-specific groups + attributes)
task.createdAila auto-creates a task from a call based on one of your task triggerstaskId, callId, triggerId, triggerName, title, body, dueDate, confidence, matchedText, status
task.syncedThe auto-created task has been pushed to a connected CRMtaskId, callId, destination (gohighlevel / bonzo / ...), destinationTaskId, destinationUrl
mismo.readyA mortgage-lending MISMO 3.4 XML file has been generatedcallId, format: "MISMO_3.4", xml (the full MISMO XML), warnings
test.pingYou click Send test event in settings, or call the test endpointendpointId, 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_KEY

The 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_KEY

Delivery 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-Id is 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:

  • t is the unix timestamp (milliseconds) at which the signature was computed
  • v1 is the hex-encoded HMAC of {timestamp}.{raw_body} using your endpoint's secret

Recommended verification steps:

  1. Parse t and v1 from the header.
  2. Reject requests older than 5 minutes (mitigates replay).
  3. Compute HMAC_SHA256(secret, "{t}.{raw_body_as_string}").
  4. Compare the hex digest to v1 with 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}/webhooks

and require a Bearer API key. See Authentication.

List webhooks

GET /api/v1/organizations/{organizationId}/webhooks

Response:

{
  "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/json

Body:

{
  "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 secretthis 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=100

Returns 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}/test

Fires 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-secret

Generates 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

  1. In Zapier, create a new Zap with the Webhooks by Zapier → Catch Hook trigger. Copy the hook URL.
  2. 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.
  3. Click Send test event — Zapier should receive it and let you pick sample fields.
  4. Add downstream Zap steps (e.g., Google Sheets, Slack, Airtable) mapping fields from the data object.

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.