Webhooks
Run event: run.started
Event type: run.started
How it is sent, signed and retried: Delivery and verification.
The first test run of the run started. 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.
Match
^[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.
Value in
"run.queued""run.started""run.needs_attention""run.completed""run.cancelled"timestamp*stringWhen the event happened (ISO 8601, UTC).
Match
^(?:(?:\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))$Format
date-timedata*What the event is about.
Delivered. Any 2xx status, within 10 seconds. The response body is ignored.
Example Requests
POST/run.started
application/json
{ "type": "run.started", "timestamp": "2026-10-30T12:00:04.456Z", "data": { "run": { "id": "3f8a6c1e-5b2d-4e7a-8c19-0d4b7e2a9f61", "app_id": "9b1d2f4e-7a3c-4c58-9e0b-6f3a1d2c4b5a", "status": "running", "outcome": null, "source": "github", "commit_sha": "4f1c2a9", "branch": "feature/login", "pr_number": 42, "repo": "acme/web", "counts": { "total": 12, "passed": 0, "failed": 0, "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"}