Webhooks
Webhooks let QA Hub notify external systems when key events occur — such as a test run completing, a bug being resolved, or a retest being required.
Supported events
| Event | Fired when |
|---|---|
test_run.completed | A test run status changes to COMPLETED |
test_run_result.failed | A result is marked FAILED in a run |
bug.resolved | A linked Linear bug reaches the done status |
retest.required | A test case is flagged for retest |
Create a webhook
Go to Settings → Developer → Webhooks → Add webhook:
- Enter the endpoint URL (must be HTTPS in production)
- Select which events to subscribe to
- Optionally add a secret for signature verification
- Click Save
Payload format
All webhook deliveries share the same envelope:
{
"event": "test_run.completed",
"timestamp": "2026-05-13T12:00:00.000Z",
"tenant_id": "cltenant001",
"data": {
// event-specific payload
}
}
test_run.completed payload
{
"event": "test_run.completed",
"data": {
"run_id": "clrun001",
"run_name": "Sprint 24 regression",
"project_id": "clxyz123",
"environment": "staging",
"total": 48,
"passed": 44,
"failed": 3,
"broken": 1,
"skipped": 0
}
}
bug.resolved payload
{
"event": "bug.resolved",
"data": {
"bug_ticket_key": "LIN-42",
"test_case_code": "TC-017",
"test_case_id": "clcase017",
"resolved_at": "2026-05-13T11:45:00.000Z"
}
}
Signature verification
Each delivery includes an X-QaHub-Signature header:
X-QaHub-Signature: sha256=<hex-encoded HMAC-SHA256>
Verify it in your endpoint using the secret configured when creating the webhook:
import crypto from 'crypto';
function verify(secret: string, payload: string, signature: string): boolean {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Retry behavior
Failed deliveries (non-2xx response or timeout) are retried up to 3 times with exponential backoff. After 3 failures, the delivery is marked as failed and no further retries occur.
Manage via API
Webhooks can also be managed via the internal API (session-authenticated):
GET /api/webhooks — List subscriptions
POST /api/webhooks — Create subscription
PATCH /api/webhooks/{id} — Update subscription
DELETE /api/webhooks/{id} — Delete subscription