Webhooks

Subscribe to events and react in real time.

Webhooks

Webhooks push events to your HTTPS endpoint as things happen in your workspace — new tickets, replies, status changes — so you can sync to your own systems without polling.

Manage endpoints

Two ways to register an endpoint:

  • Admin UI — Admin → Developer Portal → Webhooks: add your URL, pick events, copy the generated signing secret. Deliveries and retries are visible there too.
  • API — the webhook-manager function exposes GET/POST/PUT/DELETE /endpoints, POST /endpoints/:id/test, GET /deliveries and POST /deliveries/:id/retry. Authenticate with an API key that has the webhooks or admin scope (x-api-key header).

Events

  • ticket.created, ticket.updated, ticket.resolved, ticket.reopened, ticket.assigned
  • ticket.replied, ticket.replied.customer, ticket.replied.agent — agent replies carry the reply body inline in data.reply.message and are emitted for human agents, AI replies and API replies alike.
  • customer.created, customer.updated
  • chat.started, chat.ended, chat.message
  • csat.submitted, csat.rated
  • article.published, article.updated
  • webhook.test — sent by the endpoint test button.

Payload

POST https://your-app.com/webhooks/answerridge
X-AnswerRidge-Event: ticket.replied.agent
X-AnswerRidge-Timestamp: 1727350000
X-AnswerRidge-Signature: sha256=<hmac-hex>

{
  "id": "evt_<event id>",
  "type": "ticket.replied.agent",
  "tenant_id": "…",
  "created_at": "2026-09-26T12:00:00Z",
  "data": {
    "ticket": { "id": "…", "ticket_number": 15, "subject": "…", "email": "…" },
    "reply":  { "id": "…", "message": "…", "sender_type": "agent", "sender_name": "…" }
  }
}

Verify the signature

Recompute HMAC-SHA256(signing_secret, "${X-AnswerRidge-Timestamp}.${raw body}") and compare hex with the value after sha256= in X-AnswerRidge-Signature (constant-time compare). Reject timestamps more than ~5 minutes old to prevent replay.

// Node.js
import crypto from "node:crypto";
const expected = "sha256=" + crypto
  .createHmac("sha256", process.env.AR_SIGNING_SECRET)
  .update(`${req.headers["x-answerridge-timestamp"]}.${rawBody}`)
  .digest("hex");
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-answerridge-signature"]));

Retries & idempotency

Non-2xx responses retry with backoff (up to 5 attempts). Deliveries are fire-and-forget for agents — a failing endpoint never blocks a reply. Make your handler idempotent: id repeats on retries, and reply events carry the stable data.reply.id, so dedupe on that. Return any 2xx quickly and process async.