Every change to the API and the MCP server, newest first. Each entry says what a client must do and by when. The [OpenAPI description](https://monitor.agilepixel.io/docs/api/openapi.yaml)'s `info.version` is the date of the newest entry.

## 2026-10-07
The API now follows the Agile Pixel API standard, and has a public reference and an OpenAPI 3.1 description. v1 was two weeks old, so the breaking changes below apply straight away rather than through a new version.

### Breaking

- **Errors are problem details.** Every error is `application/problem+json` with `type`, `title` and `status` ([Errors](https://monitor.agilepixel.io/docs/api/errors)). The `message` member is gone; validation errors keep `errors` in the same shape. Read `status`, and `type` where it isn't `about:blank`, instead of `message`.
- **Another team's records are a 404.** A monitor or contact that belongs to another team now answers `404` instead of `403`. Treat both as "not available to this token".
- **The monitor limit is a problem.** Creating a monitor over the plan's limit is still `402`, now with the [monitor-limit-reached](https://monitor.agilepixel.io/docs/api/problems/monitor-limit-reached) type, `plan` and `max_monitors`.
- **Contact config is checked by type.** A contact's `config` must hold the keys its type needs: `email`; `webhook_url` for Slack and Discord; `url` for webhooks; `bot_token` and `chat_id` for Telegram. Other keys are dropped. Slack and Discord contacts sent with `url` used to be accepted and never alerted; send `webhook_url`.
- **List queries are validated.** On `GET /monitors`, `per_page` must be 1 to 100, and `status` and `type` must be real values; anything else is a `422`. Ask for at most 100 a page and follow `links.next`.
- **`GET /team` is wrapped in `data`**, like every other response. Read `data.plan` instead of `plan`.
- **A Viewer gets `403` before validation.** A Viewer's write request is now `403` even when its body is invalid.
- **A Viewer's token sees secrets masked.** Contact webhook URLs keep their scheme and host but end in `/********`, Telegram `bot_token` is `********`, and so are monitor header values. Use an Owner's or Admin's token where you need them.
- **MCP tool names lost their `-tool` suffix.** `list-monitors-tool` is now `list-monitors`, and so on for all eleven tools; the server is version 2.0.0. Clients that discover tools with `tools/list` need no change; prompts or scripts that name a tool do.

### Deprecated

- **`PUT` on monitors and contacts.** It behaves exactly like `PATCH` and stops working on 2027-04-07. Its responses carry `Deprecation`, `Sunset` and `Link` headers. Send `PATCH` instead.

### Added

- `GET /monitors/{monitor}` and every other monitor response include the fields a client can set: `http_method`, `expected_status_codes`, `expected_keyword`, `port`, `request_timeout_seconds`, `request_headers`, `username`, `ssl_expiry_warning_days`, plus `has_password` and `heartbeat_url`. Clients that reject unknown fields must accept these.
- `GET /monitors` includes each monitor's `notification_contacts`, and toggling a monitor returns the same fields as getting it.
- `GET /team` includes `min_interval_seconds`.
- Creating a monitor or contact returns a `Location` header. A `401` carries `WWW-Authenticate: Bearer`.
- Heartbeat pings accept `GET` and `HEAD` as well as `POST`, so Laravel's `pingOnSuccess()` and a plain `curl` work. See [Heartbeats](https://monitor.agilepixel.io/docs/api/heartbeats).
- MCP `create-monitor` and `get-monitor` return `heartbeat_url`. `create-monitor` also takes `check_interval_seconds`, `is_active`, `request_headers`, `username` and `password`, and `update-monitor` takes every field `create-monitor` does except `type`.
- This reference, the [OpenAPI 3.1 description](https://monitor.agilepixel.io/docs/api/openapi.yaml) and this changelog.

### Changed

- `PATCH` changes only the fields sent; it used to require `name`, `type` and `check_interval_minutes` every time. `null` clears an optional field.
- Changing `is_active` with `PATCH` pauses or resumes the monitor exactly as toggling it does: pausing resolves the open incident.
- Setting `username` to null removes basic auth and clears the stored password too.

### Fixed

- A malformed or out-of-range ID in the path, such as `/monitors/abc`, is a `404` instead of a `500`.
- A `404` no longer names the server's internal model class.
- A new monitor's `status` is `pending` instead of `null`.
- The MCP server's instructions named tools that didn't exist, and told agents to send `url` for Slack and Discord contacts and a bare hostname for `tcp` and `icmp` monitors. All three are corrected.

## 2026-09-23
### Added

- The REST API at `/api/v1`: monitors, notification contacts, checks, incidents and the team, with team API tokens.
- The MCP server at `/mcp`.
