Outbound Webhooks
Push ticket events to your own systems with HMAC-signed delivery, retries, and a settings UI for testing.
Webhooks let you mirror Workestra ticket activity into other systems — your CRM, Slack, an internal incident tracker, a data warehouse. Workestra POSTs a JSON payload to your endpoint every time a subscribed event happens.
Creating a Webhook
Permission required: workspace Admin or Owner.
- Go to Support > Settings > Webhooks
- Click Add webhook
- Fill in the form:
| Field | Description |
|---|---|
| Name | Human-readable label (e.g. "Slack #support-feed") |
| URL | The HTTPS endpoint that will receive POSTs |
| Events | Check the boxes for the events you want to receive |
| Active | Off by default — enable after testing |
- Click Create
- Copy the signing secret from the dialog — it's shown only once
Available Events
| Event | Fires When |
|---|---|
ticket.created | A new ticket is opened from any channel |
ticket.updated | Any field on a ticket changes |
ticket.field_changed | A specific field changes (carries before/after values) |
ticket.status_changed | Status moves between values |
ticket.priority_changed | Priority moves between values |
ticket.assigned | Assignee changes (or a ticket gets its first assignee) |
ticket.label_added | A label is added |
ticket.label_removed | A label is removed |
ticket.customer_replied | The customer adds a comment or reply |
ticket.agent_responded | An agent posts a public reply |
ticket.escalated | The ticket is manually escalated |
ticket.resolved | The ticket is moved to a resolved status |
ticket.closed | The ticket is moved to a closed status |
ticket.deleted | The ticket is archived |
ticket.sla_warning | The SLA warning threshold is crossed |
ticket.sla_breached | The SLA resolution time is exceeded |
ticket.sentiment_changed | The sentiment label moves between buckets |
You can subscribe to any subset. Most webhooks subscribe to 2–4 events relevant to their downstream system.
Payload Format
Each delivery is a POST with Content-Type: application/json:
{
"event": "ticket.status_changed",
"delivered_at": "2026-05-12T14:23:00.000Z",
"workspace_id": "wks_...",
"ticket": {
"id": "tkt_...",
"reference": "TKT-0042",
"title": "Login broken on Safari",
"status": "in_progress",
"priority": "high",
"assignee_id": "usr_...",
"customer_email": "alex@example.com"
},
"changes": {
"status": { "from": "open", "to": "in_progress" }
}
}The ticket object is the canonical ticket shape — same as the one returned by the public ticket API. The changes object is present on update-flavored events and lists what changed.
Verifying the Signature
Every delivery is signed with HMAC-SHA256 using the secret you copied at creation time. The signature is in the X-Workestra-Signature header:
X-Workestra-Signature: sha256=ab12cd34...Your endpoint should:
- Read the raw request body
- Compute
HMAC-SHA256(body, secret).hex() - Compare with the value after
sha256=using a constant-time string comparison - Reject the delivery if they don't match
Reject any delivery without a signature header — that's how you protect against spoofed POSTs to your endpoint URL.
Example (Node.js)
import crypto from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}Retries and Failures
Workestra retries failed deliveries with exponential backoff:
| Attempt | Delay after previous |
|---|---|
| 1 | — (immediate) |
| 2 | ~5 minutes |
| 3 | ~30 minutes |
| 4 | ~3 hours |
| 5 | ~12 hours |
| Final | After ~24 hours total — delivery is marked failed |
A delivery is considered successful if your endpoint returns a 2xx status code within 10 seconds. Any other response (4xx, 5xx, timeout, connection refused) counts as a failure and is retried.
Failed deliveries are visible in the Deliveries tab of the webhook detail page, with the response code, body, and timestamp.
Make your endpoint idempotent. The same event can deliver more than once if your endpoint times out and Workestra retries. Use the delivered_at timestamp + the event payload as a deduplication key on your side.
Testing a Webhook
Before flipping a new webhook to Active, send a test event:
- Open the webhook's detail page
- Click Send test
- Pick an event type from the dropdown
- Click Fire
Workestra POSTs a synthetic payload (with event: "webhook.test" and a sample ticket) to your URL and shows the response inline. Use this to confirm your endpoint is reachable, your signature verification works, and your handler doesn't crash on the canonical shape.
Disabling and Deleting
| Action | Effect |
|---|---|
| Toggle Active off | Stops new deliveries; existing failed retries are abandoned |
| Delete | Removes the webhook and its delivery history; cannot be undone |
To pause without losing history, toggle Active off. To rotate a leaked secret, delete the webhook and create a new one — Workestra doesn't support in-place secret rotation.
When NOT to Use Webhooks
- For agent-facing notifications, use Workestra's built-in inbox + email — webhooks aren't visible in the app
- For periodic exports, use the public API — webhooks deliver one event at a time
- For fetching data, webhooks push, not pull — use the API for queries
Next Steps
- Developer API — Pull current ticket state via the public API
- API Keys — Authenticate webhook handlers that call back into Workestra
- SLA Policies — The source of
ticket.sla_warningandticket.sla_breachedevents