> For the complete documentation index, see [llms.txt](https://docs.bugbug.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bugbug.io/collaboration/alerts/sending-webhook.md).

# Sending webhook

Outgoing webhooks send an HTTP request to a URL of your choice when an alert fires. Use them to connect BugBug with tools that don't have a built-in integration, or to send a custom message when the built-in [Slack](/collaboration/alerts/sending-slack-message.md) or [Discord](/integrations/discord.md) templates aren't enough.

{% hint style="info" %}
This page covers outgoing webhooks sent by alerts. To start a test or suite run from an external tool, see [incoming webhooks](/integrations/webhooks.md).
{% endhint %}

Before you start, set up the receiving endpoint in your external service. You will need its URL, and the request format it expects, to fill in the form.

## Set the method and URL

In **Method**, choose `POST` or `GET`. In **Webhook URL**, paste the address of your endpoint.

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2F102guqauEIfxPSXUTz93%2FZrzut%20ekranu%202023-09-25%20o%2014.49.29.png?alt=media&amp;token=969898a0-47f0-4b47-ad1f-83bc0178ff32" alt=""><figcaption><p>Method and Webhook URL fields</p></figcaption></figure>

You can use [variables](#variables-reference) in the URL too, for example `https://example.com/hook?run={{testRunId}}`.

## Define the request body

The **Body** field is optional. If you fill it in, it must be valid JSON. BugBug sends it with the `Content-Type: application/json` header.

Use [variables](#variables-reference) to put run details into the body. For example, an alert for finished tests can send the test name and the result:

```json
{
  "text": "{{testName}} finished with status {{status}}"
}
```

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2F5wlYUcFkpNQ0I2NpSx7M%2F2_requestBodyWithVariable.png?alt=media&amp;token=13b1e733-0bff-4941-9dc6-19819820c456" alt=""><figcaption><p>Request body with a built-in variable</p></figcaption></figure>

To start from a complete payload, click **fill with example** above the Body field. BugBug inserts an example for the event you selected in the "When" section. You can find the same payloads in [Example payloads](#example-payloads).

{% hint style="info" %}
Put string variables in quotes, e.g. `"{{testName}}"`. BugBug escapes special characters in the value, so the rendered body stays valid JSON.
{% endhint %}

## Test the webhook

Before you save the alert, click **Send test alert**. BugBug sends the request with example data and shows the response from your endpoint.

Do this every time you change the URL or body. It catches problems such as an invalid URL, missing credentials, or a body format your service doesn't accept.

<figure><img src="https://3168433179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MKixgeBPbLvnD0l1eiV%2Fuploads%2F88vc4LzDb5ao8ouPU165%2FZrzut%20ekranu%202023-09-25%20o%2015.00.29.png?alt=media&amp;token=c12b54f6-ab8a-4735-8950-1bd42dd8e47a" alt=""><figcaption><p>Verify the alert configuration</p></figcaption></figure>

## How BugBug delivers webhooks

* BugBug waits up to 30 seconds for a response and follows redirects.
* If your endpoint responds with a server error (5xx), BugBug retries the request up to 3 times. A client error (4xx) is retried once. Timeouts and connection errors are not retried.
* Retries send the same `{{eventId}}`, so you can use it to ignore duplicates.

{% hint style="warning" %}
If an endpoint responds with `410 Gone` (or with `404` and the text "unsubscribe me", which Zapier uses for deleted hooks), BugBug treats the webhook as removed. After the last retry, it disables **all webhook alerts in the project**, not only the one that failed. Fix or remove the broken alert, then turn the other webhook alerts back on in the [alerts list](/collaboration/alerts.md#manage-your-existing-alerts).
{% endhint %}

## Variables reference

All variables you can use in the webhook URL and body.

### Common variables

| Variable               | Type   | Description                                                              |
| ---------------------- | ------ | ------------------------------------------------------------------------ |
| `{{eventId}}`          | UUID   | Unique event ID for deduplication                                        |
| `{{eventName}}`        | string | Notification event (e.g., `testFinished`, `suiteFinished`)               |
| `{{status}}`           | string | Run status (`running`, `passed`, `failed`, `error`)                      |
| `{{profileName}}`      | string | Run profile name                                                         |
| `{{runMode}}`          | string | Run mode (`cloud`, `local`, `recording`)                                 |
| `{{started}}`          | string | ISO 8601 start timestamp                                                 |
| `{{finished}}`         | string | ISO 8601 end timestamp                                                   |
| `{{durationMs}}`       | int    | Duration in milliseconds (e.g., `330500`)                                |
| `{{triggeredBy}}`      | string | What started the run (e.g. `user`, `scheduler`, `api`, `webhook`, `cli`) |
| `{{organizationId}}`   | UUID   | Organization ID                                                          |
| `{{organizationName}}` | string | Organization name                                                        |
| `{{projectId}}`        | UUID   | Project ID                                                               |
| `{{projectName}}`      | string | Project name                                                             |
| `{{projectUrl}}`       | URL    | Link to project in webapp                                                |
| `{{timestampMs}}`      | int    | Current epoch timestamp in milliseconds                                  |
| `{{timestamp}}`        | int    | Alias for `timestampMs` (backward compatibility)                         |
| `{{schemaVersion}}`    | string | Webhook payload schema version (e.g., `v1`)                              |
| `{{objectsLimit}}`     | int    | Maximum number of items per list (e.g., `10`)                            |

### Test run variables

Available when the webhook is triggered by a test run event.

| Variable                  | Type   | Description                                                                         |
| ------------------------- | ------ | ----------------------------------------------------------------------------------- |
| `{{testName}}`            | string | Test name                                                                           |
| `{{testId}}`              | UUID   | Test ID                                                                             |
| `{{testRunId}}`           | UUID   | Test run ID                                                                         |
| `{{testRunUrl}}`          | URL    | Link to test run details in webapp                                                  |
| `{{testUrl}}`             | URL    | Link to test definition in webapp                                                   |
| `{{browser}}`             | string | Combined browser string (e.g., `"Chrome 120.0"`)                                    |
| `{{browserName}}`         | string | Browser name                                                                        |
| `{{browserVersion}}`      | string | Browser version                                                                     |
| `{{os}}`                  | string | Operating system name                                                               |
| `{{screenSizeType}}`      | string | Screen size type (`desktop`, `mobile`, etc.)                                        |
| `{{extensionVersion}}`    | string | BugBug extension version                                                            |
| `{{failedStepUrl}}`       | URL    | Deep-link to first failed step (only for failed/errored runs)                       |
| `{{failedStepId}}`        | UUID   | Step Id of failed step                                                              |
| `{{failedStepErrorCode}}` | string | [Error code](/debugging-tests/error-codes.md) that represents problem with the step |
| `{{suiteId}}`             | UUID   | Parent suite ID (if run within a suite)                                             |
| `{{suiteRunId}}`          | UUID   | Parent suite run ID                                                                 |
| `{{suiteName}}`           | string | Parent suite name                                                                   |
| `{{suiteRunUrl}}`         | URL    | Link to parent suite run details                                                    |
| `{{scheduleId}}`          | UUID   | Parent schedule ID (if run within a schedule)                                       |
| `{{schedulerRunId}}`      | UUID   | Parent scheduler run ID                                                             |
| `{{scheduleName}}`        | string | Parent schedule name                                                                |

### Suite run variables

Available when the webhook is triggered by a suite run event.

| Variable                    | Type   | Description                                      |
| --------------------------- | ------ | ------------------------------------------------ |
| `{{suiteId}}`               | UUID   | Suite ID                                         |
| `{{suiteRunId}}`            | UUID   | Suite run ID                                     |
| `{{suiteName}}`             | string | Suite name                                       |
| `{{suiteRunUrl}}`           | URL    | Link to suite run details                        |
| `{{suiteUrl}}`              | URL    | Link to suite configuration                      |
| `{{failedTestsCount}}`      | int    | Number of failed tests                           |
| `{{passedTestsCount}}`      | int    | Number of passed tests                           |
| `{{erroredTestsCount}}`     | int    | Number of errored tests                          |
| `{{totalTestsCount}}`       | int    | Total number of tests (including skipped)        |
| `{{failedTests}}`           | JSON   | Failed tests details (see structure below)       |
| `{{passedTests}}`           | JSON   | Passed tests details (see structure below)       |
| `{{erroredTests}}`          | JSON   | Errored tests details (see structure below)      |
| `{{failedTestsTruncated}}`  | bool   | `true` if failed tests list was truncated to 10  |
| `{{passedTestsTruncated}}`  | bool   | `true` if passed tests list was truncated to 10  |
| `{{erroredTestsTruncated}}` | bool   | `true` if errored tests list was truncated to 10 |
| `{{scheduleId}}`            | UUID   | Parent schedule ID (if run within a schedule)    |
| `{{schedulerRunId}}`        | UUID   | Parent scheduler run ID                          |
| `{{scheduleName}}`          | string | Parent schedule name                             |

### Schedule run variables

Available when the webhook is triggered by a schedule run event.

| Variable                     | Type   | Description                                       |
| ---------------------------- | ------ | ------------------------------------------------- |
| `{{scheduleId}}`             | UUID   | Schedule ID                                       |
| `{{schedulerRunId}}`         | UUID   | Scheduler run ID                                  |
| `{{scheduleName}}`           | string | Schedule name                                     |
| `{{scheduleUrl}}`            | URL    | Link to schedule configuration                    |
| `{{failedSuites}}`           | JSON   | Failed suite runs (see structure below)           |
| `{{passedSuites}}`           | JSON   | Passed suite runs (see structure below)           |
| `{{erroredSuites}}`          | JSON   | Errored suite runs (see structure below)          |
| `{{failedSuitesTruncated}}`  | bool   | `true` if failed suites list was truncated to 10  |
| `{{passedSuitesTruncated}}`  | bool   | `true` if passed suites list was truncated to 10  |
| `{{erroredSuitesTruncated}}` | bool   | `true` if errored suites list was truncated to 10 |
| `{{failedTestsCount}}`       | int    | Number of failed tests across all suites          |
| `{{passedTestsCount}}`       | int    | Number of passed tests                            |
| `{{erroredTestsCount}}`      | int    | Number of errored tests                           |
| `{{totalTestsCount}}`        | int    | Total number of tests (including skipped)         |
| `{{failedTests}}`            | JSON   | Failed tests details                              |
| `{{passedTests}}`            | JSON   | Passed tests details                              |
| `{{erroredTests}}`           | JSON   | Errored tests details                             |
| `{{failedTestsTruncated}}`   | bool   | `true` if failed tests list was truncated to 10   |
| `{{passedTestsTruncated}}`   | bool   | `true` if passed tests list was truncated to 10   |
| `{{erroredTestsTruncated}}`  | bool   | `true` if errored tests list was truncated to 10  |

## JSON structures

### `failedTests` / `passedTests` / `erroredTests`

All three lists share the same structure:

```json
[
  {
    "testId": "<uuid>",
    "testRunId": "<uuid>",
    "testName": "Login Test",
    "status": "failed",
    "testRunUrl": "https://app.bugbug.io/.../runs-history/tests/<test_run_id>/",
    "started": "2026-03-24T10:00:00+00:00",
    "finished": "2026-03-24T10:05:30+00:00",
    "durationMs": 330500,
    "browser": "Chrome 120.0",
    "browserName": "Chrome",
    "browserVersion": "120.0",
    "os": "Linux",
    "screenSizeType": "desktop",
    "extensionVersion": "1.2.3"
  }
]
```

**Note:** Test lists are limited to 10 entries to prevent oversized payloads.

### `failedSuites` / `passedSuites` / `erroredSuites`

Available only in schedule run events:

```json
[
  {
    "suiteRunId": "<uuid>",
    "suiteRunUrl": "https://app.bugbug.io/.../runs-history/suites/<suite_run_id>/",
    "suiteId": "<uuid>",
    "suiteName": "'MAIN' Suite",
    "status": "failed",
    "started": "2026-03-24T10:00:00+00:00",
    "finished": "2026-03-24T10:05:30+00:00",
    "durationMs": 330500
  }
]
```

{% hint style="warning" %}
**Important:** Use list variables (`failedTests`, `passedTests`, `erroredTests`) **without quotes** in the template to get native JSON arrays in the payload. The template itself won't be valid JSON, but the rendered output will be.
{% endhint %}

{% hint style="warning" %}
**Important:** For numeric and boolean variables (counts, `*Truncated`, etc.) use them **without quotes** to preserve types.
{% endhint %}

## Example payloads

These are the same payloads that **fill with example** inserts in the alert form.

**Test Run**

```
{
  "id": "{{eventId}}",
  "schemaVersion": "{{schemaVersion}}",
  "eventType": "{{eventName}}",
  "timestampMs": {{timestampMs}},
  "organization": {
    "id": "{{organizationId}}",
    "name": "{{organizationName}}"
  },
  "project": {
    "id": "{{projectId}}",
    "name": "{{projectName}}"
  },
  "test": {
    "id": "{{testId}}",
    "name": "{{testName}}"
  },
  "execution": {
    "id": "{{testRunId}}",
    "status": "{{status}}",
    "started": "{{started}}",
    "finished": "{{finished}}",
    "durationMs": {{durationMs}},
    "triggeredBy": "{{triggeredBy}}",
    "profileName": "{{profileName}}",
    "runMode": "{{runMode}}",
    "browser": "{{browser}}",
    "browserName": "{{browserName}}",
    "browserVersion": "{{browserVersion}}",
    "os": "{{os}}",
    "screenSizeType": "{{screenSizeType}}",
    "extensionVersion": "{{extensionVersion}}"
  },
  "parent": {
    "suiteId": "{{suiteId}}",
    "suiteName": "{{suiteName}}",
    "suiteRunId": "{{suiteRunId}}",
    "scheduleId": "{{scheduleId}}",
    "scheduleName": "{{scheduleName}}",
    "schedulerRunId": "{{schedulerRunId}}"
  },
  "links": {
    "project": "{{projectUrl}}",
    "testRun": "{{testRunUrl}}",
    "test": "{{testUrl}}",
    "suiteRun": "{{suiteRunUrl}}",
    "failedStep": "{{failedStepUrl}}"
  },
  "failedStep": {
    "stepId": "{{failedStepId}}",
    "errorCode": "{{failedStepErrorCode}}"
  }
}
```

**Suite Run**

```
{
  "id": "{{eventId}}",
  "schemaVersion": "{{schemaVersion}}",
  "eventType": "{{eventName}}",
  "timestampMs": {{timestampMs}},
  "organization": {
    "id": "{{organizationId}}",
    "name": "{{organizationName}}"
  },
  "project": {
    "id": "{{projectId}}",
    "name": "{{projectName}}"
  },
  "suite": {
    "id": "{{suiteId}}",
    "name": "{{suiteName}}"
  },
  "execution": {
    "id": "{{suiteRunId}}",
    "status": "{{status}}",
    "started": "{{started}}",
    "finished": "{{finished}}",
    "durationMs": {{durationMs}},
    "triggeredBy": "{{triggeredBy}}",
    "profileName": "{{profileName}}",
    "runMode": "{{runMode}}"
  },
  "parent": {
    "scheduleId": "{{scheduleId}}",
    "scheduleName": "{{scheduleName}}",
    "schedulerRunId": "{{schedulerRunId}}"
  },
  "links": {
    "project": "{{projectUrl}}",
    "suiteRun": "{{suiteRunUrl}}",
    "suite": "{{suiteUrl}}"
  },
  "results": {
    "total": {{totalTestsCount}},
    "counts": {
      "passed": {{passedTestsCount}},
      "failed": {{failedTestsCount}},
      "errored": {{erroredTestsCount}}
    }
  },
  "failedTests": {
    "truncated": {{failedTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{failedTests}}
  },
  "passedTests": {
    "truncated": {{passedTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{passedTests}}
  },
  "erroredTests": {
    "truncated": {{erroredTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{erroredTests}}
  }
}
```

**Schedule Run**

```
{
  "id": "{{eventId}}",
  "schemaVersion": "{{schemaVersion}}",
  "eventType": "{{eventName}}",
  "timestampMs": {{timestampMs}},
  "organization": {
    "id": "{{organizationId}}",
    "name": "{{organizationName}}"
  },
  "project": {
    "id": "{{projectId}}",
    "name": "{{projectName}}"
  },
  "schedule": {
    "id": "{{scheduleId}}",
    "name": "{{scheduleName}}"
  },
  "execution": {
    "id": "{{schedulerRunId}}",
    "status": "{{status}}",
    "started": "{{started}}",
    "finished": "{{finished}}",
    "durationMs": {{durationMs}},
    "profileName": "{{profileName}}",
    "runMode": "{{runMode}}"
  },
  "links": {
    "project": "{{projectUrl}}",
    "schedule": "{{scheduleUrl}}"
  },
  "failedSuites": {
    "truncated": {{failedSuitesTruncated}},
    "limit": {{objectsLimit}},
    "items": {{failedSuites}}
  },
  "passedSuites": {
    "truncated": {{passedSuitesTruncated}},
    "limit": {{objectsLimit}},
    "items": {{passedSuites}}
  },
  "erroredSuites": {
    "truncated": {{erroredSuitesTruncated}},
    "limit": {{objectsLimit}},
    "items": {{erroredSuites}}
  },
  "results": {
    "total": {{totalTestsCount}},
    "counts": {
      "passed": {{passedTestsCount}},
      "failed": {{failedTestsCount}},
      "errored": {{erroredTestsCount}}
    }
  },
  "failedTests": {
    "truncated": {{failedTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{failedTests}}
  },
  "passedTests": {
    "truncated": {{passedTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{passedTests}}
  },
  "erroredTests": {
    "truncated": {{erroredTestsTruncated}},
    "limit": {{objectsLimit}},
    "items": {{erroredTests}}
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bugbug.io/collaboration/alerts/sending-webhook.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
