Checking your session…

Chat API & Webhooks

Two ways to wire conversations into your own software: talk to the assistant directly over HTTP (drive it from your own UI, test prompts from CI, let another system converse with it), and be told when conversations finish via a webhook (post transcripts to your CRM, trigger follow-ups, feed analytics).

Available to any signed-in member of the account that owns the app, and to scoped API keys with the chat capability. Runs a synchronous chat exchange against the app's prompt.

Response (200):

Pass "chatId" on subsequent calls to maintain conversation context.

POST/api/v1/apps/{appId}/chat
curl -X POST https://api.everycallhandled.com/api/v1/apps/$APP_ID/chat \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{ "message": "Hello, can I book an appointment?" }'
Sign in to run this against your account.
JSON
{
  "reply": "Of course! What date and time works for you?",
  "chatId": "..."
}

Apps can configure a postChatCallback attribute that fires after each completed conversation. Useful for:

  • Posting transcripts to your CRM
  • Triggering follow-up automation
  • Custom analytics

Set the callback by PATCHing the app:

Payload (POSTed to your URL after each call):

JSON
{
  "attributes": {
    "postChatCallback": {
      "uri": "https://your-server.com/webhook",
      "method": "POST",
      "headers": { "Authorization": "Bearer YOUR_INTERNAL_TOKEN" }
    }
  }
}
JSON
{
  "appId": "...",
  "chatId": "...",
  "callerIdPhone": "+44...",
  "callbackPhone": "+44...",
  "callerName": "...",
  "callerEmail": "...",
  "summary": "Caller booked an appointment for...",
  "urgency": "normal",
  "sentiment": 8,
  "callDuration": 145,
  "transcript": [ { "speaker": "...", "text": "...", "timestamp": "..." } ],
  "patientContext": { /* populated when the Semble integration is connected */ },
  "bookingsMade": [ /* if a booking integration is connected */ ]
}

Timeout: each delivery attempt times out after 5 seconds. Your endpoint should acknowledge with a 2xx response inside that window — even a bare 200 OK is fine; we don't need a body. Slow processing (writing to your CRM, triggering downstream automations) should happen asynchronously after you've already responded; treating webhook delivery as an inbox-write pattern is the safest shape.

Retry policy: failed deliveries (any non-2xx response, connection refused, or timeout) are retried with exponential backoff:

Attempt Delay after previous
1 immediate (first try)
2 ~30 seconds
3 ~2 minutes
4 ~10 minutes
5 ~1 hour
6 (final) ~6 hours

After 6 failed attempts, the platform stops trying for that conversation and records the failure internally. We do not notify your account by default; if you need failure notifications, talk to support.

Idempotency: every delivery carries the same (appId, chatId) tuple — if you receive the same payload twice (e.g. a retry fired even though your endpoint succeeded but the connection dropped before we read the response), de-duplicate on chatId. Payload content never changes between retries, so a second delivery for a known chatId is safe to drop.

Signature verification: the platform signs each request with HMAC-SHA256 over the JSON body, included as an X-ILQ-Signature header. The signing key is generated when you configure the callback and returned as the webhookSigningSecret field in the PATCH /apps/{id} response that sets postChatCallback (it starts with whsec_); store it server-side. If you set the callback up before signing was available, re-save it once (any PATCH that sets postChatCallback) to generate the key — deliveries stay unsigned, exactly as before, until you do. Verify on receipt:

Without verification you can't tell a real platform delivery from a third party who's discovered your callback URL. We strongly recommend verifying every payload.

Failure-mode summary:

Scenario What happens
Your endpoint returns 2xx Delivery succeeded; no retry.
Your endpoint returns 4xx Treated as permanent failure; retries cease after attempt 1. (Reason: 4xx usually means malformed payload from our side; retrying won't help.)
Your endpoint returns 5xx Retried per the backoff table above.
Connection refused / DNS failure Retried per the backoff table above.
Timeout (>5s, no response) Retried per the backoff table above.
All 6 retries exhausted Recorded on our side; not delivered to your account.

The call itself succeeds regardless of webhook outcome. Webhook delivery is asynchronous to the call; a failed webhook never affects the caller's experience or your billing. The transcript + summary + analytics are still stored in your account and queryable via the /conversations endpoints — the webhook is a convenience, not the source of truth.

Other events: the post-call webhook is the only customer-subscribable push notification today. For billing and usage events, poll GET /api/v1/billing/merchant or the ledger endpoints on your own schedule. A general-purpose subscribable event stream is on the roadmap but not shipped.


python
import hmac, hashlib
expected = hmac.new(signing_key.encode(), request.body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get('X-ILQ-Signature', '')):
    return 401