Skip to main content
Outgoing webhooks push each tool submission to your server as it happens, signed with an HMAC so you can prove it came from us. Same shape as Stripe’s: a timestamp and a signature in one header, a tolerance window on your side. Deliveries are retried with exponential backoff, and every attempt is inspectable after the fact — including a manual redelivery.

Events

An endpoint receives only the events it subscribes to.

POST /tenants//webhooks

Register an endpoint. Required scope: settings:write
The signing secret is returned once, at creation. We store a copy to sign with, but the API never returns it again — if you lose it, delete the endpoint and create a new one.
The url must be publicly resolvable https. It is checked against an SSRF guard (DNS resolved, private ranges refused) at registration.

The payload

input holds the fields your tool declared, under your field names. channel is web, voice, api (an agent called the API) or form (the standalone form widget).

messages — the conversation, when we already have it

The last 10 turns of the conversation that produced the submission, so you don’t have to call back to find out what the visitor actually asked. It is present when the turns are in memory at delivery time — the chat path. It is absent for the form widget (channel: "form", there is no conversation) and may be absent on other channels: we do not add a database read to a fire-and-forget path that runs for every tool execution. Treat it as optional and fall back to conversationId + GET /tenants//conversations/ when you need the full thread. Only user and assistant turns are included — never system prompts or tool calls.

Keeping visitor PII off the wire

piiFields lists keys to remove from data.input before signing and sending — for example ["visitorEmail", "visitorPhone"] if your CRM must not receive contact details over this channel. Max 32 entries.
Setting any piiFields also removes messages entirely. A transcript is free text: the visitor typed their email into it, so redacting a key while shipping the conversation verbatim would defeat the request. The unredacted payload is still kept in our database for your audit trail — only the wire is trimmed.

Verifying the signature

Every request carries three headers: v1 is HMAC_SHA256(secret, "<t>.<raw request body>"), hex-encoded. Sign the raw body — re-serializing your parsed JSON will not match. Reject the request if |now - t| > 300 seconds: without that check, a captured request can be replayed forever.

Delivery, retries and timeouts

Answer with any 2xx and quickly: the request is aborted after 5 seconds. Do the work after acknowledging, not before. A non-2xx, a timeout or a connection error schedules a retry. Six attempts total, spaced 30 s, 2 min, 10 min, 1 h, 6 h — about 8 hours of tolerance for an endpoint that is down. After the last failure the delivery is marked failed and stays inspectable. Retries mean the same event can arrive twice (for instance if your 2xx was lost on the way back). Deduplicate on X-Animam-Delivery-Id, or on data.id if you prefer to key on the submission.

GET /tenants//webhooks

List endpoints. Secrets are never included. Scope: settings:read

GET /tenants//webhooks/

One endpoint, with its recent delivery stats. Scope: settings:read

PATCH /tenants//webhooks/

Change url, description, events, enabled or piiFields. Scope: settings:write Disabling an endpoint ("enabled": false) stops delivery immediately; deliveries already pending are dropped rather than queued forever.

DELETE /tenants//webhooks/

Remove the endpoint and its delivery history. Scope: settings:write

GET /tenants//webhooks//deliveries

The delivery log: status, attempt count, HTTP status, last error, timestamps. This is where you look when your endpoint says nothing arrived. Scope: settings:read

POST /tenants//webhooks//deliveries//redeliver

Queue a past delivery again — same payload, freshly signed (the timestamp moves, so the old signature would not have verified anyway). Use it after fixing your receiver rather than asking a visitor to submit again. Scope: settings:write