# Webhooks

> Semaloop sends a webhook when a test run finishes, so your own tools can react. One event type today: test_run.status_changed.v1.

Rendered version: https://docs.semaloop.com/integrations/webhooks/

Semaloop webhooks notify your own tooling the moment a test run finishes, so you can kick off whatever should happen next — open a ticket, page someone, or point a coding agent at the failure. To get started, go to **Settings** → **Webhooks** and choose **Set up webhooks**.

From the webhook portal you can add endpoint URLs, choose which events each endpoint receives, inspect every delivery and its response, and replay failed deliveries. Webhooks are delivered by [Svix](https://www.svix.com).

Every delivery is signed. Each endpoint has a signing secret (shown in the portal) — use it to verify the `svix-signature` header before trusting a request, either with a [Svix SDK](https://docs.svix.com/receiving/verifying-payloads/how) or by [verifying the signature manually](https://docs.svix.com/receiving/verifying-payloads/how-manual).

## Events

There is currently one event type:

* `test_run.status_changed.v1` — a test run reached a terminal status: `success`, `failure` or `error`.

```json
{
  "type": "test_run.status_changed.v1",
  "id": "a379af73-217d-464b-b4ee-266b63a1f48d.failure",
  "timestamp": "2026-09-01T10:14:32.000Z",
  "appId": "uk_example_app",
  "orgId": "org_01ABC…",
  "data": {
    "previousStatus": "pending",
    "nextStatus": "failure",
    "testRunId": "a379af73-217d-464b-b4ee-266b63a1f48d",
    "testId": "0b2c6a1e-9f7d-4c3a-8e21-5d4f6a7b8c9d",
    "buildId": "3f1e2d4c-5b6a-4789-9c0d-1e2f3a4b5c6d",
    "url": "https://app.semaloop.com/app/uk_example_app/run/a379af73-…",
    "failureReason": "Tapping 'Sign in' showed an error despite valid credentials",
    "errorReason": null,
    "completedAt": "2026-09-01T10:14:31.000Z"
  }
}
```

Only wanting failures? Add a [transformation](https://docs.svix.com/transformations) to the endpoint in the portal (the endpoint’s **Advanced** tab) so non-failures are never delivered at all:

```js
function handler(webhook) {
  if (webhook.payload.data.nextStatus !== "failure") {
    webhook.cancel = true;
  }
  return webhook;
}
```

A run that fails and is automatically retried by Semaloop only sends an event for the final attempt. `id` is stable per run and status, so you can safely deduplicate on it.

## Related

- Index of every Semaloop docs page: https://docs.semaloop.com/llms.txt
- All Semaloop docs in one file: https://docs.semaloop.com/llms-full.txt
