hello ai
Sign inStart free trial
Developer docs

Webhooks

Hello AI POSTs signed JSON events to your endpoint as calls happen, so your CRM, ticketing system, or automation tool stays in sync without polling. Configure the endpoint in Dashboard → Settings → Webhooks.

Delivery

How events arrive

Verification

Verify every delivery

The signature is an HMAC-SHA256 of `${timestamp}.${rawBody}` with your signing secret (shown once when you save the webhook). Node example:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyHelloAiWebhook(req: { headers: Record<string, string>; rawBody: string }, secret: string): boolean {
  const ts = req.headers["x-helloai-timestamp"];
  const sig = req.headers["x-helloai-signature"]; // "sha256=<hex>"
  if (!ts || !sig) return false;
  // Reject anything older than 5 minutes (replay protection).
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = "sha256=" + createHmac("sha256", secret).update(ts + "." + req.rawBody).digest("hex");
  return expected.length === sig.length && timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}

Events

Event reference

call.started

A call to your Hello AI number is beginning.

{
  "id": "evt_9c2f…",
  "event": "call.started",
  "createdAt": "2026-07-12T02:14:07Z",
  "tenantId": "561c…",
  "data": {
    "callId": "c1a2…",
    "callerNumber": "+12015550142",
    "toNumber": "+16575550110",
    "startedAt": "2026-07-12T02:14:07Z"
  }
}
call.ended

The call finished. Includes the transcript and AI summary, so you can file the whole record without polling.

{
  "event": "call.ended",
  "data": {
    "callId": "c1a2…",
    "callerNumber": "+12015550142",
    "startedAt": "…", "endedAt": "…",
    "durationSeconds": 184,
    "endedReason": "customer-ended-call",
    "afterHours": true,
    "summary": "Caller requested an appointment for…",
    "transcript": "AI: Thank you for calling…\nCaller: Hi, I'd like to…"
  }
}
request.created

A structured appointment or prescription-refill request was captured. kind is "appointment" or "refill".

{
  "event": "request.created",
  "data": {
    "requestId": "7f3b…",
    "kind": "refill",
    "urgency": "routine",
    "callerName": "Pat Doe",
    "callerPhone": "+12015550142",
    "medication": "lisinopril 10mg",
    "pharmacy": "CVS Cedar Grove",
    "notes": "Medication: lisinopril 10mg · Pharmacy: CVS…",
    "afterHours": false,
    "callId": "c1a2…"
  }
}
escalation.started

An urgent after-hours call entered the on-call cascade (medical practices).

{
  "event": "escalation.started",
  "data": {
    "escalationId": "e88a…",
    "callerName": "Pat Doe",
    "callerNumber": "+12015550142",
    "reason": "post-op bleeding",
    "callId": "c1a2…"
  }
}

A pingevent with the same envelope is sent by the "Send test event" button in Settings.