Creating a subscription #
Administrators manage subscriptions under Admin → Settings → Webhooks. A subscription has a target URL, a set of event types, and optional filters: organizations, projects, assignee, and whether internal ticket messages are included.
On creation you receive the signing secret once, in the form whsec_…. Afterwards only a hint is shown. You can rotate it at any time.
The payload #
{
"id": "evt_01J8…",
"type": "task.status_changed",
"version": "2026-09-01",
"occurredAt": "2026-09-01T09:24:00Z",
"organization": { "id": "…", "slug": "trackr" },
"project": { "id": "…", "key": "SCM" },
"actor": { "id": "…", "name": "Ertugul Kilic" },
"data": {
"task": { "id": "…", "ref": "SCM-12", "title": "…" },
"from": "in_progress",
"to": "done"
}
}Long text fields are truncated to 2000 characters. The version is the envelope version; it changes only when the shape changes.
Headers and signature #
| Header | Content |
|---|---|
X-Trackr-Event | The event type, e.g. ticket.created. |
X-Trackr-Delivery | Unique id of this delivery. Use it to de-duplicate. |
X-Trackr-Timestamp | Unix time (seconds) the request was signed. |
X-Trackr-Signature | sha256=<hex>: HMAC-SHA256 of timestamp + "." + body with your secret. |
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret: string, timestamp: string, body: string, header: string) {
const expected =
'sha256=' + createHmac('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(header);
return a.length === b.length && timingSafeEqual(a, b);
}Reject requests whose timestamp is older than a few minutes to prevent replays.
Events #
| Group | Event types |
|---|---|
| Tickets | ticket.created · ticket.status_changed · ticket.assigned · ticket.message_created · ticket.closed |
| Tasks | task.created · task.updated · task.status_changed · task.assigned · task.unassigned · task.deleted · task.time_logged |
| Projects & members | project.created · project.archived · project.member_added · project.member_removed · organization.member_added · organization.member_removed · organization.member_role_changed · invitation.accepted |
| Chat (opt-in) | thread.created · thread.tagged · message.created |
A synthetic ping event is sent by Send test event and cannot be subscribed to.
Delivery, retries and disabling #
- Deliveries are made by the worker with a 10-second timeout. Any
2xxcounts as success. - Failed deliveries are retried after 1 min → 5 min → 30 min → 2 h → 12 h (six attempts total).
- A subscription is disabled automatically after five consecutive exhausted deliveries with no success in 72 hours. Admins are notified and can re-enable it.
- Every attempt is recorded and can be inspected or redelivered from the subscription page.