Ticket Webhooks
Webhooks let your helpdesk notify external systems when ticket events happen. Configure one endpoint URL per organization and subscribe to the events you care about.
Setup
- Open Settings → Webhooks
- Enter your HTTPS endpoint URL.
- Enable webhooks and select events.
- Save — a signing secret is generated. Copy it immediately; it is not shown again.
- Use Send test webhook to verify your endpoint receives payloads.
Webhooks are outbound (we call your URL). For inbound REST access to tickets, use API Keys.
Events
ticket.opened— A new ticket is created.ticket.status_changed— Ticket status changes (e.g. open → resolved).ticket.customer_reply— A customer sends a public reply (portal, email, or customer API). Staff replies and internal notes do not trigger this event.
Request format
We send an HTTP POST with JSON body and these headers:
Content-Type: application/jsonX-Webhook-Event— event nameX-Webhook-Timestamp— Unix timestamp (seconds)X-Webhook-Signature—sha256=<hmac>
Return any HTTP 2xx status to acknowledge success. Non-2xx responses are retried up to three times with backoff.
Payload envelope
{
"event": "ticket.status_changed",
"occurred_at": "2026-09-01T10:30:00+00:00",
"tenant": { "subdomain": "your-org" },
"data": {
"ticket": {
"id": 42,
"ticket_number": "TKT-001",
"subject": "Login issue",
"status": "pending",
"priority": "normal",
"customer": {
"id": 7,
"name": "Jane Doe",
"email": "jane@example.com"
}
},
"status_change": { "from": "open", "to": "pending" }
}
}
Customer reply example
{
"event": "ticket.customer_reply",
"occurred_at": "2026-09-01T10:35:00+00:00",
"tenant": { "subdomain": "your-org" },
"data": {
"ticket": { ... },
"message": {
"id": 99,
"body": "<p>Thanks, still broken</p>",
"body_text": "Thanks, still broken",
"author_type": "customer",
"created_at": "2026-09-01T10:35:00+00:00"
}
}
}
Verifying signatures
Compute HMAC-SHA256 over {timestamp}.{raw_json_body} using your signing secret. Compare to the X-Webhook-Signature header (value format: sha256=...).
PHP example
$timestamp = (int) $request->header('X-Webhook-Timestamp');
$signature = $request->header('X-Webhook-Signature');
$body = $request->getContent();
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
if (! hash_equals($expected, $signature)) {
abort(401, 'Invalid signature');
}
Security
- Use HTTPS endpoints in production.
- Rotate the signing secret if it may have been exposed.
- Reject requests with timestamps too far from the current time (replay protection).
- Do not log full secrets or unredacted payloads in public systems.