# Notifications

> How to choose where a test's notifications go, from an agent or in the app, and what each message says when a test goes down, recovers, is rate limited or clears.

A test sends a notification when its state changes, not on every run: when it goes `DOWN`, when
it turns `RATE_LIMITED`, and when it comes back to `UP` from either. When each change happens is
on [Runs, states and uptime](/api-testing/runs-and-uptime/#how-states-change).

All tests share the channels of the **API tests** source, and any single test can be sent
somewhere else instead. Unrouted, a test follows the source; an unrouted source goes to your email
channel plus your default Slack channel. The precedence rules are on
[Alerts](/using-fixter/alerting/#where-alerts-arrive).

:::note[Before you start]
A Slack channel has to be connected to Fixter before any test can deliver to it, and that is done
in the app, not from a test: [Settings > Integrations](https://app.fixter.dev/settings/integrations),
steps on [Slack](/setup/slack/). Until you connect one, the only channel is your email channel,
and that is where every notification goes.
:::

## Choose channels with your agent

Channels first: [`list_alert_channels`](/reference/mcp/tools/#list_alert_channels) returns every
connected channel with its `id`, email included.

- **All tests**:
  [`set_notification_destinations`](/reference/mcp/tools/#set_notification_destinations) with
  source `e2e-monitoring` and the channel ids.
- **One test**:
  [`set_api_test_destinations`](/reference/mcp/tools/#set_api_test_destinations) with the test
  id and channel ids. An empty list switches that test's notifications off; leaving `channelIds`
  out puts it back on the source's routing. `create_api_test` also takes `channelIds`.
- **Read a test's routing**:
  [`get_api_test_destinations`](/reference/mcp/tools/#get_api_test_destinations), with a test id
  for one test or without for every test routed on its own.

```text
Send the "Checkout API" test's notifications to #payments-alerts only.
```

## Choose channels in the app

1. **Connect your channels** under
   [Settings > Integrations](https://app.fixter.dev/settings/integrations); the Slack steps are on
   [Slack](/setup/slack/). Your email channel is there from the start.

   <!-- fx-image: Settings > Integrations with a Slack channel connected and the email channel listed -->

2. **Choose channels for all tests** under [Settings > Notifications](https://app.fixter.dev/settings/notifications):
   pick the channels for the **API tests** source.

   <!-- fx-image: Settings > Notifications with the API tests source row and its channel picker open -->

3. **Choose channels for one test** from its `Delivers to` column on the
   [API Tests page](https://app.fixter.dev/api-tests), or from `Delivers to` on the test's own
   page. Choose channels for that test, or none to switch its notifications off.

   <!-- fx-image: the API Tests list with the Delivers to column open for one test -->

The API Tests page always says where failing runs go, `Failing test runs go to <destinations>.`,
with a `Change` link. When nothing is delivered anywhere it says
`API test notifications are off. Failing runs will not reach anyone until you choose a destination.`

<!-- fx-image: the API Tests page banner reading "Failing test runs go to ..." with its Change link -->

## What each notification says

A test sends four kinds of notification, in two pairs: down and recovered, rate limited and no
longer rate limited. Each arrives in the channels chosen above, in Slack and by email alike.

### Down

Sent when the test goes `DOWN`, from `UP` or from `RATE_LIMITED`. The title is the test's name
followed by the kind of failure that turned it down:

| Run cause | Title |
|---|---|
| `ASSERTION` | `<name> failed its checks` |
| `DNS`, `TLS`, `CONNECT` | `<name> is unreachable` |
| `BLOCKED_ADDRESS` | `<name> points at a private address` |
| `TIMEOUT` | `<name> timed out` |
| Any other cause, or none | `<name> failed` |

Under the title: the first failed assertion in words as the subtitle (for example
`Response body is not valid JSON`), or the run message as the body when no assertion failed (for
example `DNS lookup for the target host failed`); every failed assertion in a code block; chips
for the type, the target, `failing since <HH:mm> UTC` and the streak (`3 failed runs in a row,
each after 2 retries`); and a `View test` button.

### Recovered

Sent when a `DOWN` test returns to `UP`. Title `Recovered` followed by the test's name, body
`Passing again.`, with `Failed for` (how long it was not passing), `Since`, and for an HTTP test
the `Status code` and `Latency` of the passing run.

### Rate limited

Sent when the test turns `RATE_LIMITED`, after as many rate-limited runs in a row as its failure
threshold. Title `<name> is being rate limited`, subtitle `The target answered HTTP 429 to the
monitor`, chips for the type, the target and `since <HH:mm> UTC`, and the `View test` button. A
rate-limited test is not down and those runs do not count against uptime. If the 429s come from a
CDN, WAF or rate limiter in front of your service, see
[Allow tests through your firewall](/api-testing/firewalls/).

### No longer rate limited

Sent when a rate-limited test passes again. Title `No longer rate limited: <name>`, body
`Passing again.`, with `Rate limited for` and `Since`.

<!-- fx-image: a Slack channel showing a down message, its recovery message and a rate-limited message for an API test -->

## What an MCP failure says

An MCP test that fails its assertions goes down with the title `<name> failed its checks`, and the
subtitle is the first failed assertion in words:

| Assertion | Subtitle |
|---|---|
| `mcp.initialize` | `MCP server did not identify itself` |
| `mcp.tools.contains` | `Tool <name> is missing` |
| `mcp.tool.isError` | `Tool <toolName> returned an error` |
| `mcp.protocol.revision` | `Server no longer speaks protocol <pin>` |
| `maxLatencyMillis` | `Took <actual> ms, limit <limit> ms` |

`Server no longer speaks protocol <pin>` comes only from a test pinned to one revision; see
[A pinned revision](/api-testing/mcp-protocol-revisions/#a-pinned-revision). A `Newest supported`
test that moves to a different revision is judged normally and sends nothing for the move. What
each assertion checks is on [MCP tests](/api-testing/mcp-tests/#what-a-failed-run-says).

## Auto-investigate

Every API test notification is tagged as an alert. In a Slack channel with auto-investigate on, a
down message and a rate-limited message each start an investigation, just as an alert does,
unless one of the channel's exclusion patterns matches it. The two recovery messages never start
one. Email carries no investigations. Turning auto-investigate on is on
[Slack](/setup/slack/#turn-on-investigations).

## If the cause is your firewall

If the 429s or the failures come from your own CDN, WAF or rate limiter rather than from the
service behind it, let the tests through as
[Allow tests through your firewall](/api-testing/firewalls/) describes.

Still stuck? Email [info@fixter.dev](mailto:info@fixter.dev) and one of our founders will help you fix your problem asap.