Resources

Webhooks

.md

Receive ticket, message and inbox events over HTTPS and verify their signatures.

Inboxes can be configured with webhook endpoints that receive events as JSON via HTTPS POST. Configure them in the inbox settings or via the API with the webhooks field of POST /v1/inboxes or PATCH /v1/inboxes/{id}. Each event is only sent to the webhooks of the inbox it belongs to.

PATCH /v1/inboxes/{id}
JSON
01{
02 "webhooks": [
03 {
04 "url": "https://example.com/tickos/webhook",
05 "events": ["ticket.created", "message.created"],
06 "enabled": true
07 }
08 ]
09}
UTF-8 · LF · JSONtickos.dev

A signing secret (whsec_...) is generated for each webhook that does not have one and is returned in the inbox webhooks array. The webhooks field replaces the whole list: to keep an existing webhook and its secret, send it back with its secret; an entry sent without a secret gets a new one.

Events

EventFired when
ticket.createdA new ticket is created in the inbox: via POST /v1/tickets, the dashboard, an inbound email, a form submission, the public portal or a scheduling booking. Tickets started with POST /v1/tickets/compose do not fire it.
ticket.updatedA ticket is edited via PATCH /v1/tickets/{id} or through the equivalent ticket edit in the dashboard. Actions performed with /v1/tickets/{id}/manage, and status changes caused by new messages, do not fire it.
ticket.deletedA ticket is permanently deleted (DELETE /v1/tickets/{id} or from the dashboard).
message.createdA message is added to a ticket: via POST /v1/tickets/{id}/messages (drafts are skipped until sent with /send), the dashboard (internal notes excluded), an inbound customer email or the public portal.
inbox.updatedThe inbox settings are changed (PATCH /v1/inboxes/{id} or the dashboard).
inbox.deletedThe inbox is deleted (DELETE /v1/inboxes/{id} or the dashboard).

Delivery

Each delivery is a POST request with a JSON body and these headers:

HeaderValue
Content-Typeapplication/json
User-AgentTickOS-Webhooks/1.0
X-Webhook-EventThe event name, e.g. ticket.created.
X-Tickos-Signaturet={unix_timestamp},v1={hmac_sha256_hex}. Only sent when the webhook has a secret.

The body is an envelope with the event name, an ISO 8601 timestamp and the event data. data always includes the inbox; ticket events add ticket and customer, message events add message and ticket, and inbox events carry the inbox fields.

ticket.created
JSON
01{
02 "event": "ticket.created",
03 "timestamp": "2026-10-13T15:04:05.000Z",
04 "data": {
05 "inbox": { "id": "inb_...", "name": "Support", "email_address": "support@yourdomain.com" },
06 "ticket": {
07 "id": "tkv_...",
08 "client_id": "SUP-000123",
09 "subject": "Cannot access my account",
10 "status": "open",
11 "priority": "normal",
12 "shared_uuid": "shr_...",
13 "public_url": "https://app.tickos.dev/ticket/shr_...",
14 "metadata": {},
15 "created_at": "2026-10-13T15:04:04.000Z",
16 "updated_at": "2026-10-13T15:04:04.000Z"
17 },
18 "customer": { "id": "cus_...", "email": "jane@example.com", "name": "Jane Doe", "phone": null }
19 }
20}
UTF-8 · LF · JSONtickos.dev
  • Respond with any 2xx status to acknowledge the delivery.
  • Each delivery times out after 10 seconds.
  • Deliveries are attempted once: failed or timed-out deliveries are not retried. Use the API (e.g. GET /v1/tickets?updated_after=...) to reconcile if your endpoint was unavailable.
  • Events are sent independently, so they may arrive out of order. Use timestamp and the resource updated_at to order them.

Verifying signatures

When the webhook has a secret, every delivery carries an X-Tickos-Signature header containing a timestamp and an HMAC-SHA256 signature:

request.http
HTTP
01X-Tickos-Signature: t={unix_timestamp},v1={hmac_sha256_hex}
UTF-8 · LF · HTTPtickos.dev

To verify, compute HMAC-SHA256(secret, "{timestamp}.{raw_body}") and compare it with v1 using a constant-time comparison. Use the raw request body exactly as received, before parsing it as JSON:

example.js
JavaScript
01import crypto from 'node:crypto'
02 
03function verify(rawBody, header, secret) {
04 const [tPart, sigPart] = header.split(',')
05 const t = tPart.split('=')[1]
06 const sig = sigPart.split('=')[1]
07 const expected = crypto
08 .createHmac('sha256', secret)
09 .update(`${t}.${rawBody}`)
10 .digest('hex')
11 return sig.length === expected.length &&
12 crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
13}
UTF-8 · LF · JavaScripttickos.dev
Replay protection
Reject deliveries whose timestamp is older than 5 minutes to prevent replay attacks.

Automation webhook steps

The webhook step of an automation is a separate mechanism: it calls the URL configured in the step (with its own method, headers and optional custom payload) when a contact reaches that step. Its default payload uses the event name automation.step.webhook and a different structure (automation, enrollment, customer and trigger data). See the Automations page for details.