Run event: run.completed
Event type: run.completed
How it is sent, signed and retried: Delivery and verification.
Every test run ended and none was stopped. data.run.outcome is passed, failed or error: failed if any test failed, else error if any errored, was blocked or inconclusive, else passed. Sent once per run.
webhook-id*stringThe message id. The same on every retry of the message, so use it to drop duplicates.
webhook-timestamp*stringUnix seconds when this attempt was signed.
^[0-9]+$webhook-signature*stringv1, for each active secret, separated by a space. Two during the 24 hours after a secret rotation; accept the message if either matches.
The message body. Verify the signature against the raw bytes.
application/json- body
The body of a run event message. It is thin on purpose: fetch details from api_url.
type*stringThe event type.
"run.queued""run.started""run.needs_attention""run.completed""run.cancelled"timestamp*stringWhen the event happened (ISO 8601, UTC).
^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$date-timedata*What the event is about.
Delivered. Any 2xx status, within 10 seconds. The response body is ignored.
{ "type": "run.completed", "timestamp": "2026-10-30T12:03:41.001Z", "data": { "run": { "id": "3f8a6c1e-5b2d-4e7a-8c19-0d4b7e2a9f61", "app_id": "9b1d2f4e-7a3c-4c58-9e0b-6f3a1d2c4b5a", "status": "completed", "outcome": "failed", "source": "github", "commit_sha": "4f1c2a9", "branch": "feature/login", "pr_number": 42, "repo": "acme/web", "counts": { "total": 12, "passed": 10, "failed": 2, "error": 0 }, "url": "https://app.outqube.com/acme/runs?execution=3f8a6c1e-5b2d-4e7a-8c19-0d4b7e2a9f61", "api_url": "https://app.outqube.com/api/v1/runs/3f8a6c1e-5b2d-4e7a-8c19-0d4b7e2a9f61" } }}{ "webhook-id": "string", "webhook-timestamp": "string", "webhook-signature": "string"}