Guides
Get a signed webhook on every event
Register an endpoint, verify the signature and timestamp on every call, choose your events, and handle retries and the dead-letter state.
On this page
AI Assistant can call your own system on every event that matters — a conversation starts, a message arrives, a visitor asks for a person, an identity is verified, a tool runs. Each call is a signed HTTPS POST you verify and act on however you like. This guide sets one up end to end.
1. Register an endpoint
In Console → Connections, open Webhooks and add an endpoint. Give it a public https:// address that your server controls, pick the events you want, and save. The platform generates a signing secret and shows it once — copy it now and store it where your server reads its secrets. You will not see it again; if you lose it, rotate the endpoint for a new one.
2. Verify every call
Each delivery carries three headers: X-Busymate-Timestamp (unix seconds), X-Busymate-Signature (t=<timestamp>,v1=<hex>), and X-Busymate-Delivery (a stable id). Recompute the signature as an HMAC-SHA256 over the exact string <timestamp>.<raw request body> using your signing secret, and compare it to the v1 value in constant time. Reject the call if it does not match, and reject it if the timestamp is more than five minutes from your clock — that window stops an old call being replayed.
3. Choose your events
Subscribe only to what you use. The available events are conversation.started, message.received, handoff.requested, handoff.resolved, identity.verified, and tool.called; a subscription of * receives all of them. Every payload has the same envelope: the event name, your tenant id, an occurred_at timestamp, and a data object with the ids for that event. Change the list any time from the same card.
4. Send a test event
Use "Send test event" on the endpoint to queue a webhook.test delivery. It is signed exactly like a real one, so it proves your verification code before any real traffic depends on it. If you want to inspect the raw call first, the card offers a hosted capture URL that records what it receives so you can read the exact headers and body.
5. Retries and the dead-letter state
Delivery is at-least-once: because a call can be retried, the same X-Busymate-Delivery id may arrive more than once, so treat that id as an idempotency key and ignore a repeat. A call that does not return a 2xx is retried with growing backoff; after several failed attempts it moves to a dead-letter state you can see in the same card, with the last status and error. Fix your endpoint, then retry a dead-lettered delivery from there.
Verify
- Confirm the endpoint shows as saved in Connections with your chosen events.
- Send a test event and confirm your server receives a
webhook.testPOST. - Confirm your code accepts the signature and rejects a tampered body.
- Confirm a call whose timestamp is older than five minutes is rejected.
- Point the endpoint at a broken URL, send a test event, and confirm it retries and then shows in the dead-letter state.
Questions
Where do I get the signing secret?
It is generated when you create the endpoint and shown once in that same response. Rotate the endpoint to get a new one; the old secret stops working immediately.
What exactly do I sign to verify a call?
The string
timestamp.body— the value ofX-Busymate-Timestamp, a literal dot, then the raw request body — with HMAC-SHA256 under your signing secret. Compare the hex result to thev1part ofX-Busymate-Signature.Why did I receive the same event twice?
Delivery is at-least-once, so a retry can repeat an event. Use
X-Busymate-Deliveryas an idempotency key and skip a delivery id you have already handled.What happens if my endpoint is down?
The call is retried with exponential backoff. After several failures it is dead-lettered and stops retrying; you can see it, fix your endpoint, and retry it from Connections.
Can I manage endpoints without the Console?
Yes. The same actions are on the MCP server as
set_webhook_endpoint,get_webhook_status,list_webhook_deliveriesand related tools, so an agent or script can manage them too.