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

  1. Open Settings → Webhooks
  2. Enter your HTTPS endpoint URL.
  3. Enable webhooks and select events.
  4. Save — a signing secret is generated. Copy it immediately; it is not shown again.
  5. 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/json
  • X-Webhook-Event — event name
  • X-Webhook-Timestamp — Unix timestamp (seconds)
  • X-Webhook-Signaturesha256=<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.