Webhooks

Webhooks

Webhooks let MAIA notify your server about call events in real time. The platform sends an HTTP POST request to the URL you configure, containing the event data and an HMAC signature for authenticity verification.

Events

When creating an endpoint you choose which events it subscribes to:

EventWhen it fires
call.startedThe conversation began: outbound — the callee picked up, inbound — the call was answered
call.completedThe call finished and post-processing is ready (summary, goal, extracted fields)
call.no_answerNo answer: nobody picked up within the ring timeout, or no conversation took place
call.transferredThe agent transferred the call to a human operator
call.failedThe call ended with an error
lead.createdText channel: a lead was collected (dialog goal reached)

call.completed also carries an amd field: "voicemail" — an answering machine picked up, "bot" — a robot was on the other end, null — a regular conversation. Integrations can use it to reschedule unreachable contacts automatically.

Configuration

Webhooks are configured per project in the dashboard at dashboard.maia-ai.com. For each project, specify:

  • Webhook URL — the address of your HTTPS endpoint that MAIA will POST to.
  • Secret — the string used to sign requests (HMAC). Keep it safe and use it to verify the signature.

Configure webhooks separately for each project in the dashboard. Knowledge base management is also available through the public REST API.

Example payload

The request body is JSON. The field set may expand, so write your code to tolerate additional fields.

{
  "id": "delivery_uuid",
  "event": "call.completed",
  "created_at": "2026-07-02T10:00:00+00:00",
  "data": {
    "call_id": "call_uuid",
    "project_id": "project_uuid",
    "contact_id": "contact_uuid",
    "direction": "outbound",
    "phone": "+79001234567",
    "status": "completed",
    "duration_sec": 124,
    "goal_reached": true,
    "summary": "Booked for Thursday, 3 pm",
    "extracted": { "name": "Anna", "datetime": "2026-07-03T15:00" },
    "amd": null
  }
}
  • id — the delivery identifier (use it to deduplicate retries).
  • data.call_id — the call identifier (same as in GET /v1/public/calls/{call_id}, where the transcript and recording are available).
  • data.goal_reached / data.summary / data.extracted — post-processing results.
  • data.amd — "voicemail" / "bot" / null (see above).

For call.started and call.transferred the data object is shorter: identifiers, direction, phone; transfers also include transfer_to and reason.

Verifying the HMAC signature

Every request includes an HMAC signature in a header. Compute the HMAC over the raw request body using your secret and compare the result with the signature from the header. Compare signatures with a constant-time function to resist timing attacks.

⚠️

Verify the signature against the raw request body bytes, before any parsing or JSON reformatting. Parsing and re-serializing can change the bytes and break verification.

Pseudocode:

import hmac, hashlib
 
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

If the signature does not match, return 401 and do not process the request.

Best practices

  • Respond 2xx quickly. Return 200/2xx immediately upon receipt, and run heavy processing asynchronously (queue, background job). A slow response may cause a timeout and redelivery.
  • Idempotency. The same call may be delivered more than once (retries on network failures). Deduplicate by the call id so you don’t process one event twice.
  • Always verify the signature. Do not trust the request body without a successful HMAC check.
  • Use HTTPS. Your webhook endpoint must be reachable over HTTPS.

See also