Webhooks
Webhooks let external systems subscribe to events that happen in Lathe Studio — test run completions, build creations, release status changes, and more.
Availability: Webhooks are included for everyone, with no limit on the number of endpoints — during the 90-day preview and after it. You can create a subscription from Settings → Webhooks or through the v1 API; both are available throughout the preview.
Access stops only when a subscription lapses: an expired preview is refused everywhere, and an account that cancelled after paying keeps read access while losing writes.
Event Taxonomy#
All events follow the naming convention: {resource}.{action}.{version}
| Event | Trigger |
|---|---|
testrun.created.v1 | A new test run is created |
testrun.assigned.v1 | A test run is assigned, reassigned, unassigned, or moved to/from the unassigned queue (UI or API) |
testrun.submitted.v1 | A tester submits a test run |
testrun.completed.v1 | A test run is marked completed (after build completion) |
testcase.failed.v1 | A test case is recorded as failed in an execution |
build.created.v1 | A new build is created via API, UI, or webhook |
release.released.v1 | A release status changes to released |
Event Payload Structure#
All events share a common envelope:
{
"id": "evt_ba3be321f0f72ca1127f065e",
"event": "testrun.submitted.v1",
"created_at": "2026-03-01T14:32:00Z",
"data": { ... }
}
testrun.assigned.v1
{
"id": "evt_9709bd5bb00284c1f0232a28",
"event": "testrun.assigned.v1",
"created_at": "2026-09-24T09:15:00Z",
"data": {
"run_id": "70d987d9-65b9-51ff-a0c6-c17d9d7bc9aa",
"project_id": "f791a8d1-efe3-5a71-8d47-2ad8c527caad",
"assigned_to": "1aa31dfe-ba5e-5cc4-8c14-6a75a3531011",
"previous_assignee": null,
"assigned_by": "0216d7ce-d6db-5bb9-b03e-ea0ebed60562",
"assigned_by_api_key": null
}
}
assigned_to/previous_assigneeare internal user UUIDs, ornullfor the unassigned / queue states.assigned_byis the assigning user (UI), ornullwhen the actor is an API key.assigned_by_api_keyis{ "id", "name" }when the assignment came through the API (e.g.POST /api/v1/testruns/{id}/assign, or a create / split with an assignee), andnullfor UI assignments. Additive — existing consumers can ignore it.
Behaviour change (2026-09-24, touchless T3): testrun.assigned.v1 now
fires from every assignment path — the UI, the new
POST /api/v1/testruns/{id}/assign, creating a run with assignee_id or
assignee_email (T4), and splitting a run with an assignee. Previously only
UI assignments emitted it; API-created assigned runs and splits sent nothing.
testrun.submitted.v1
{
"id": "evt_921305ceebee62053ccb620a",
"event": "testrun.submitted.v1",
"created_at": "2026-03-01T14:32:00Z",
"data": {
"run_id": "70d987d9-65b9-51ff-a0c6-c17d9d7bc9aa",
"project_id": "f791a8d1-efe3-5a71-8d47-2ad8c527caad",
"build_id": "68551e3f-da51-5dfa-b099-971f44b2dbf0",
"release_id": "68619492-1b42-5d3c-b0f7-ad73247577bc",
"submitted_by": "1aa31dfe-ba5e-5cc4-8c14-6a75a3531011",
"results_summary": {
"pass": 42,
"fail": 3,
"blocked": 1,
"skipped": 4,
"total": 50
}
}
}
build.created.v1
{
"id": "evt_f763f7ee0ae769d9f989095e",
"event": "build.created.v1",
"created_at": "2026-03-01T12:00:00Z",
"data": {
"build_id": "df5f0ffd-ddc0-5a78-84c2-c0b52b90908b",
"project_id": "f791a8d1-efe3-5a71-8d47-2ad8c527caad",
"name": "v2.4.1",
"commit_sha": "a1b2c3d4",
"branch": "main",
"source": "api"
}
}
Behaviour change (2026-09-24, touchless T1): build.created.v1 now fires from every build-creation path — the UI, POST /api/v1/builds, results ingest, and the GitHub, GitLab, Jenkins and Bitbucket webhook receivers. Previously only the UI and POST /builds emitted it. It fires exactly once per build, only when the build is created — retries and dedupe hits (same CI run re-delivered, or an Idempotency-Key replay) emit nothing. The source field reflects the intake path (ui, api, ingest, github, gitlab, jenkins, bitbucket).
Payloads are self-contained — they include all the data you need to act without making follow-up API calls.
Signature Verification#
Every webhook delivery includes an X-Lathe-Signature header. Verify this signature before processing the payload to ensure it came from Lathe Studio.
The signature is an HMAC-SHA256 digest of the raw request body, keyed with your webhook secret:
X-Lathe-Signature: sha256=a1b2c3d4e5f6...
Verification Examples
Node.js:
import crypto from 'crypto'
function verifyWebhookSignature(
rawBody: string,
signatureHeader: string,
secret: string
): boolean {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
)
}
Python:
import hmac
import hashlib
def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
Always use a timing-safe comparison (timingSafeEqual / hmac.compare_digest) to prevent timing attacks.
In-Product Signature Verifier
Organization Settings → Webhooks includes a built-in signature verifier. Paste your webhook secret and a sample payload to compute the expected X-Lathe-Signature value without writing code. Use it to debug failed verifications or validate your endpoint logic before going live.
Registering a Webhook Endpoint#
Webhook registration is managed in Organization Settings → Webhooks.
To register an endpoint:
- Enter your endpoint URL (must be HTTPS)
- Select the events to subscribe to
- Save — a signing secret is generated and shown once
Your endpoint must respond with a 2xx status within 10 seconds or the delivery is treated as failed.
Retry Policy#
Failed deliveries (non-2xx response, timeout, or connection error) are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 (initial) | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 12 hours |
After 5 failed attempts, the event is sent to the dead-letter queue and delivery is stopped. You can view and replay dead-letter events from the Webhooks settings page.
Developer Skill#
Download the Lathe Studio Developer Skill for copy-paste signature verification examples, retry schedules, and CI/CD integration recipes in Node.js, Python, and Go.
Best Practices#
- Respond immediately, process asynchronously. Return
200 OKimmediately and process the payload in a background job to avoid timeouts. - Handle duplicate deliveries. Use
evt.idto deduplicate — the same event may be delivered more than once after retries. - Verify signatures. Never process a webhook without first verifying the signature.
- Monitor the dead-letter queue. Check failed deliveries periodically in Org Settings.