Webhooks for AI customer support events
OctaDezx webhooks POST signed JSON events such as chat.escalated, order.created and conversation.handed_off to your HTTPS endpoint, with an outbox, retries and HMAC-SHA256 verification.
Status: available. OctaDezx webhooks POST JSON events to your HTTPS endpoint, signed with HMAC-SHA256 over the timestamp and the raw body. Each endpoint has its own signing secret and an optional event list. Delivery runs through an outbox with retries every five minutes, is at least once, and disables an endpoint after 20 consecutive failures. Events include chat.escalated, order.created and voicemail.received on every channel, and conversation.answered and conversation.handed_off from the Agent API.
Delivery
Configure endpoints in Dashboard > Settings > Webhooks. Requests time out after 10 seconds and redirects are not followed. Return a 2xx once you have durably recorded the event, and deduplicate on id.
Headers
POST <your url>
Content-Type: application/json
User-Agent: OctaDezx-Webhooks/1.0
X-OctaDezx-Event: conversation.handed_off
X-OctaDezx-Delivery: 81723
X-OctaDezx-Timestamp: 1793750400
X-OctaDezx-Signature: sha256=<hex>Body
{
"id": "81723",
"event": "conversation.handed_off",
"business_id": "...",
"created_at": "2026-11-03T10:00:00.000Z",
"data": { "conversation_id": "...", "status": "handed_off", "channel": "api", "user_id": "u_123", "reply": "...", "reason": "Refund over limit" }
}Verify the signature
X-OctaDezx-Signature is sha256= plus hex HMAC-SHA256, keyed with the endpoint secret, over the timestamp, a dot and the raw body. Reject anything older than five minutes, and always verify the raw bytes; parsing and re-serializing changes them.
Node.js
import { verifyWebhookEvent } from "@octadezx/sdk";
app.post("/octadezx/webhook", express.raw({ type: "application/json" }), (req, res) => {
const event = verifyWebhookEvent(req.body, req.headers as any, process.env.OCTADEZX_WEBHOOK_SECRET!);
// dedupe on event.id, then act
res.sendStatus(204);
});Python
from octadezx import verify_webhook_event
event = verify_webhook_event(raw_body, headers, secret)Events
conversation.* events are emitted by the Agent API only in this release. For website widget and messaging channel handoffs, subscribe to chat.escalated.
| Event | Emitted by | data |
|---|---|---|
| conversation.answered | Agent API turns with status answered or needs_input | conversation_id, status, channel, user_id, reply |
| conversation.handed_off | Agent API turns with status handed_off | The same, plus reason |
| chat.escalated | Any channel, when the AI hands a conversation to a person | session_id, reason, customer_name, customer_email, last_message, ai_reply |
| order.created | Orders from chat, widget, API or voice | Order fields |
| voicemail.received | Phone calls | Voicemail fields |
Frequently asked questions
- How are OctaDezx webhooks signed? With HMAC-SHA256 over the timestamp, a dot and the raw body, keyed with the endpoint's secret, sent as X-OctaDezx-Signature: sha256=<hex>.
- Are webhooks retried? Yes. Delivery uses an outbox, failed deliveries are retried every five minutes, and an endpoint is disabled after 20 consecutive failures. Delivery is at least once, so deduplicate on id.
- Which event tells me a conversation needs a person? chat.escalated on every channel, and conversation.handed_off for Agent API conversations.
- Can I choose which events an endpoint receives? Yes. Each endpoint has an optional list of events; an empty list means all.