A monitor is something AgileMonitor checks on a schedule: a URL, a certificate, a port, a host, or a job that pings it. When a check fails, the monitor goes down, an [incident](https://monitor.agilepixel.io/docs/api/incidents) opens, and its [notification contacts](https://monitor.agilepixel.io/docs/api/notification-contacts) are alerted. When it passes again, the incident resolves and the contacts hear that too.

## Monitor types
| Type | Needs | Up when |
|---|---|---|
| `http` | `url` | The URL answers with one of `expected_status_codes` (default 200) |
| `keyword` | `url`, `expected_keyword` | As `http`, and the body contains the keyword |
| `ssl` | `url` | The certificate is valid; warns `ssl_expiry_warning_days` before it expires |
| `tcp` | `url`, `port` | A TCP connection to the URL's host on the port succeeds |
| `icmp` | `url` | The URL's host answers ping |
| `heartbeat` | nothing | Its job requested the `heartbeat_url` within the check interval. See [Heartbeats](https://monitor.agilepixel.io/docs/api/heartbeats) |

`url` is always a full URL, such as `https://shop.example.com`, even for `tcp` and `icmp`, which use only its host. A bare hostname is rejected. The type can't change after the monitor is created.

## The monitor object
Every monitor response has every field below, in this order. Clients that reject unknown fields should expect new ones to be added; see the [changelog](https://monitor.agilepixel.io/docs/api/changelog).

| Field | Type | Description |
|---|---|---|
| `id` | integer | The monitor's ID. |
| `name` | string | The name shown in the dashboard and in alerts. |
| `type` | string | What to check. One of `http`, `ssl`, `heartbeat`, `keyword`, `tcp`, `icmp`. |
| `url` | string or null | The URL checked. `tcp` and `icmp` monitors use only its host; heartbeat monitors have none. |
| `status` | string | The latest result. One of `pending`, `up`, `down`, `degraded`. |
| `is_active` | boolean | false while the monitor is paused. |
| `check_interval_minutes` | integer | How often the monitor is checked, in minutes. One of `1`, `5`, `15`, `30`, `60`. |
| `check_interval_seconds` | integer or null | An exact interval in seconds that overrides `check_interval_minutes`, or null. |
| `http_method` | string | The HTTP method used for `http` and `keyword` checks. One of `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`. |
| `expected_status_codes` | array of integer | HTTP status codes that count as up. |
| `expected_keyword` | string or null | Text a `keyword` monitor's response body must contain. |
| `port` | integer or null | The port a `tcp` monitor connects to. |
| `request_timeout_seconds` | integer | How long a check waits for an answer before counting as down. |
| `request_headers` | object or null | Extra request headers, name to value. A Viewer's token sees each value as `********`. |
| `username` | string or null | The HTTP basic-auth user name. |
| `has_password` | boolean | Whether a basic-auth password is stored. The password itself is never returned. |
| `ssl_expiry_warning_days` | integer | How many days before the certificate expires to warn. |
| `last_checked_at` | string or null | When the monitor was last checked, in UTC, or null before its first check. |
| `ssl_days_remaining` | integer or null | Days until the certificate expires, for monitors whose certificate has been read. |
| `ssl_expires_at` | string or null | When the certificate expires, in UTC. |
| `heartbeat_token` | string or null | A heartbeat monitor's token. Treat it as a secret; anyone with it can ping the monitor. |
| `heartbeat_url` | string or null | The URL a heartbeat monitor's job requests after each run. |
| `open_incident` | Incident or null | The incident open now, or null when the monitor is not down. |
| `notification_contacts` | array of NotificationContact | The contacts alerted when this monitor goes down or recovers. |
| `created_at` | string | When the monitor was created, in UTC. |

## List monitors
`GET /api/v1/monitors`

Lists the team's monitors in name order, a page at a time, with each monitor's open incident and alert contacts. Filter by `status` or `type`.

| | |
|---|---|
| Authentication | Bearer token. Any member of the team |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `page` | query | integer | No | The page to return, from 1. Defaults to 1. |
| `per_page` | query | integer | No | Monitors per page, from 1 to 100. Defaults to 25. |
| `status` | query | string | No | Only monitors with this status. One of `pending`, `up`, `down`, `degraded`. |
| `type` | query | string | No | Only monitors of this type. One of `http`, `ssl`, `heartbeat`, `keyword`, `tcp`, `icmp`. |

### Example request

```bash
curl https://monitor.agilepixel.io/api/v1/monitors \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Accept: application/json"
```

### Responses

| Status | When | Body |
|---|---|---|
| `200` | A page of monitors. | A page of [monitors](#monitor-object) |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `422` | The request body or query is invalid. `errors` lists the messages for each field. | [Validation problem](https://monitor.agilepixel.io/docs/api/problems/validation-failed) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 200

```json
{
  "data": [
    {
      "id": 48214,
      "name": "Nightly backup",
      "type": "heartbeat",
      "url": null,
      "status": "up",
      "is_active": true,
      "check_interval_minutes": 60,
      "check_interval_seconds": 90000,
      "http_method": "GET",
      "expected_status_codes": [200],
      "expected_keyword": null,
      "port": null,
      "request_timeout_seconds": 30,
      "request_headers": null,
      "username": null,
      "has_password": false,
      "ssl_expiry_warning_days": 14,
      "last_checked_at": "2026-10-07T09:14:00.000000Z",
      "ssl_days_remaining": null,
      "ssl_expires_at": null,
      "heartbeat_token": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "heartbeat_url": "https://monitor.agilepixel.io/ping/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "open_incident": null,
      "notification_contacts": [],
      "created_at": "2026-09-23T10:05:12.000000Z"
    },
    {
      "id": 48213,
      "name": "Storefront",
      "type": "http",
      "url": "https://shop.example.com",
      "status": "up",
      "is_active": true,
      "check_interval_minutes": 5,
      "check_interval_seconds": null,
      "http_method": "GET",
      "expected_status_codes": [200],
      "expected_keyword": null,
      "port": null,
      "request_timeout_seconds": 30,
      "request_headers": null,
      "username": null,
      "has_password": false,
      "ssl_expiry_warning_days": 14,
      "last_checked_at": "2026-10-07T09:15:00.000000Z",
      "ssl_days_remaining": null,
      "ssl_expires_at": null,
      "heartbeat_token": null,
      "heartbeat_url": null,
      "open_incident": null,
      "notification_contacts": [
        {
          "id": 9312,
          "name": "Ops email",
          "type": "email",
          "config": {
            "email": "ops@example.com"
          },
          "notify_on_down": true,
          "notify_on_recovery": true
        }
      ],
      "created_at": "2026-09-23T10:02:41.000000Z"
    }
  ],
  "links": {
    "first": "https://monitor.agilepixel.io/api/v1/monitors?page=1",
    "last": "https://monitor.agilepixel.io/api/v1/monitors?page=1",
    "prev": null,
    "next": null
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 1,
    "links": [
      {
        "url": null,
        "label": "&laquo; Previous",
        "page": null,
        "active": false
      },
      {
        "url": "https://monitor.agilepixel.io/api/v1/monitors?page=1",
        "label": "1",
        "page": 1,
        "active": true
      },
      {
        "url": null,
        "label": "Next &raquo;",
        "page": null,
        "active": false
      }
    ],
    "path": "https://monitor.agilepixel.io/api/v1/monitors",
    "per_page": 25,
    "to": 2,
    "total": 2
  }
}
```

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 422

```json
{
  "type": "https://monitor.agilepixel.io/docs/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The url field is required unless type is in heartbeat.",
  "errors": {
    "url": ["The url field is required unless type is in heartbeat."]
  }
}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```

## Create a monitor
`POST /api/v1/monitors`

Creates a monitor and starts checking it. A heartbeat monitor gets a `heartbeat_url` for its job to request after each run. Alerts go only to the contacts in `notification_contact_ids`.

Fails with 402 when the team already has as many monitors as its plan allows: call `GET /team` first and check `can_add_monitor`.

| | |
|---|---|
| Authentication | Bearer token. Owners and Admins; Viewers get 403 |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Request body

| Field | Type | Required | Rules |
|---|---|---|---|
| `name` | string | Yes | The name shown in the dashboard and in alerts. Up to 255 characters. |
| `type` | string | Yes | What to check. One of `http`, `ssl`, `heartbeat`, `keyword`, `tcp`, `icmp`. |
| `url` | string or null | No | A full URL, up to 500 characters. `tcp` and `icmp` use only its host; a bare hostname is rejected. |
| `check_interval_minutes` | integer | Yes | How often to check, in minutes. At least the plan's `min_interval_minutes`. One of `1`, `5`, `15`, `30`, `60`. |
| `check_interval_seconds` | integer or null | No | An exact interval in seconds that overrides the minutes. At least the plan's `min_interval_seconds`. |
| `is_active` | boolean | No | Start paused with false. Defaults to true. |
| `http_method` | string | No | The HTTP method. Defaults to GET. One of `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`. |
| `expected_status_codes` | array of integer | No | HTTP status codes that count as up. Defaults to [200]. |
| `expected_keyword` | string or null | No | Required for `keyword` monitors. Up to 255 characters. |
| `port` | integer or null | No | Required for `tcp` monitors. 1 to 65535. |
| `request_timeout_seconds` | integer | No | Seconds to wait for an answer. Defaults to 30. 1 to 60. |
| `request_headers` | object or null | No | Extra request headers, name to value. |
| `username` | string or null | No | An HTTP basic-auth user name. Up to 255 characters. |
| `password` | string or null | No | An HTTP basic-auth password. Stored encrypted and never returned. Up to 255 characters. |
| `ssl_expiry_warning_days` | integer | No | Days before the certificate expires to warn. Defaults to 14. 1 to 90. |
| `notification_contact_ids` | array of integer or null | No | IDs of the team's contacts to alert. |

### Example request

```bash
curl -X POST https://monitor.agilepixel.io/api/v1/monitors \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Storefront",
    "type": "http",
    "url": "https://shop.example.com",
    "check_interval_minutes": 5,
    "notification_contact_ids": [9312]
  }'
```

### Responses

| Status | When | Body |
|---|---|---|
| `201` | The monitor was created. | The [monitor](#monitor-object), in `data` |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `402` | The team already has as many monitors as its plan allows. | [Monitor limit problem](https://monitor.agilepixel.io/docs/api/problems/monitor-limit-reached) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `422` | The request body or query is invalid. `errors` lists the messages for each field. | [Validation problem](https://monitor.agilepixel.io/docs/api/problems/validation-failed) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 201

```json
{
  "data": {
    "id": 48213,
    "name": "Storefront",
    "type": "http",
    "url": "https://shop.example.com",
    "status": "pending",
    "is_active": true,
    "check_interval_minutes": 5,
    "check_interval_seconds": null,
    "http_method": "GET",
    "expected_status_codes": [200],
    "expected_keyword": null,
    "port": null,
    "request_timeout_seconds": 30,
    "request_headers": null,
    "username": null,
    "has_password": false,
    "ssl_expiry_warning_days": 14,
    "last_checked_at": null,
    "ssl_days_remaining": null,
    "ssl_expires_at": null,
    "heartbeat_token": null,
    "heartbeat_url": null,
    "open_incident": null,
    "notification_contacts": [
      {
        "id": 9312,
        "name": "Ops email",
        "type": "email",
        "config": {
          "email": "ops@example.com"
        },
        "notify_on_down": true,
        "notify_on_recovery": true
      }
    ],
    "created_at": "2026-10-07T09:12:30.000000Z"
  }
}
```

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 402

```json
{
  "type": "https://monitor.agilepixel.io/docs/api/problems/monitor-limit-reached",
  "title": "Monitor limit reached",
  "status": 402,
  "detail": "The Starter plan allows 20 monitors and this team has 20. Delete a monitor or upgrade the plan.",
  "plan": "starter",
  "max_monitors": 20
}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 422

```json
{
  "type": "https://monitor.agilepixel.io/docs/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The url field is required unless type is in heartbeat.",
  "errors": {
    "url": ["The url field is required unless type is in heartbeat."]
  }
}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```

## Get a monitor
`GET /api/v1/monitors/{monitor}`

Returns one of the team's monitors with its open incident and alert contacts. A monitor that belongs to another team is a 404.

| | |
|---|---|
| Authentication | Bearer token. Any member of the team |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `monitor` | path | integer | Yes | The monitor's ID. |

### Example request

```bash
curl https://monitor.agilepixel.io/api/v1/monitors/48213 \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Accept: application/json"
```

### Responses

| Status | When | Body |
|---|---|---|
| `200` | The monitor. | The [monitor](#monitor-object), in `data` |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `404` | Nothing with that ID belongs to the team, or the path doesn't exist. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 200

```json
{
  "data": {
    "id": 48213,
    "name": "Storefront",
    "type": "http",
    "url": "https://shop.example.com",
    "status": "up",
    "is_active": true,
    "check_interval_minutes": 5,
    "check_interval_seconds": null,
    "http_method": "GET",
    "expected_status_codes": [200],
    "expected_keyword": null,
    "port": null,
    "request_timeout_seconds": 30,
    "request_headers": null,
    "username": null,
    "has_password": false,
    "ssl_expiry_warning_days": 14,
    "last_checked_at": "2026-10-07T09:15:00.000000Z",
    "ssl_days_remaining": null,
    "ssl_expires_at": null,
    "heartbeat_token": null,
    "heartbeat_url": null,
    "open_incident": null,
    "notification_contacts": [
      {
        "id": 9312,
        "name": "Ops email",
        "type": "email",
        "config": {
          "email": "ops@example.com"
        },
        "notify_on_down": true,
        "notify_on_recovery": true
      }
    ],
    "created_at": "2026-09-23T10:02:41.000000Z"
  }
}
```

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 404

```json
{"type": "about:blank", "title": "Not Found", "status": 404}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```

## Update a monitor
`PATCH /api/v1/monitors/{monitor}`

`PUT /api/v1/monitors/{monitor}` is the same partial update. It is deprecated and stops working on 7 April 2027; its responses carry `Deprecation`, `Sunset` and `Link` headers. Use PATCH.

Changes only the fields sent; the rest keep their value. Send `null` to clear an optional field. The type can't change. `notification_contact_ids` replaces the monitor's whole set of contacts. Sending `check_interval_minutes` without `check_interval_seconds` clears the seconds override. Changing `is_active` pauses or resumes the monitor exactly as toggling it does.

| | |
|---|---|
| Authentication | Bearer token. Owners and Admins; Viewers get 403 |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `monitor` | path | integer | Yes | The monitor's ID. |

### Request body

| Field | Type | Required | Rules |
|---|---|---|---|
| `name` | string | No | The name shown in the dashboard and in alerts. Up to 255 characters. |
| `url` | string or null | No | A full URL, up to 500 characters. Only a heartbeat monitor may have none. |
| `check_interval_minutes` | integer | No | How often to check, in minutes. Clears `check_interval_seconds` unless that is sent too. One of `1`, `5`, `15`, `30`, `60`. |
| `check_interval_seconds` | integer or null | No | An exact interval in seconds that overrides the minutes, or null to remove it. |
| `is_active` | boolean | No | false pauses the monitor and resolves its open incident; true resumes it, like toggling it. |
| `http_method` | string | No | The HTTP method. One of `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`. |
| `expected_status_codes` | array of integer | No | HTTP status codes that count as up. At least one. |
| `expected_keyword` | string or null | No | Text the body must contain. Can't be null on a `keyword` monitor. Up to 255 characters. |
| `port` | integer or null | No | The port to connect to. Can't be null on a `tcp` monitor. 1 to 65535. |
| `request_timeout_seconds` | integer | No | Seconds to wait for an answer. 1 to 60. |
| `request_headers` | object or null | No | Replacement request headers, or null to remove them. |
| `username` | string or null | No | The basic-auth user name, or null to remove basic auth, which clears the password too. Up to 255 characters. |
| `password` | string or null | No | A new basic-auth password. Empty or null keeps the stored one. Up to 255 characters. |
| `ssl_expiry_warning_days` | integer | No | Days before the certificate expires to warn. 1 to 90. |
| `notification_contact_ids` | array of integer or null | No | The complete new set of contacts to alert. An empty list or null removes them all. |

### Example request

```bash
curl -X PATCH https://monitor.agilepixel.io/api/v1/monitors/48213 \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Storefront (checkout)", "check_interval_minutes": 1}'
```

### Responses

| Status | When | Body |
|---|---|---|
| `200` | The updated monitor. | The [monitor](#monitor-object), in `data` |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `404` | Nothing with that ID belongs to the team, or the path doesn't exist. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `422` | The request body or query is invalid. `errors` lists the messages for each field. | [Validation problem](https://monitor.agilepixel.io/docs/api/problems/validation-failed) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 200

```json
{
  "data": {
    "id": 48213,
    "name": "Storefront",
    "type": "http",
    "url": "https://shop.example.com",
    "status": "up",
    "is_active": true,
    "check_interval_minutes": 5,
    "check_interval_seconds": null,
    "http_method": "GET",
    "expected_status_codes": [200],
    "expected_keyword": null,
    "port": null,
    "request_timeout_seconds": 30,
    "request_headers": null,
    "username": null,
    "has_password": false,
    "ssl_expiry_warning_days": 14,
    "last_checked_at": "2026-10-07T09:15:00.000000Z",
    "ssl_days_remaining": null,
    "ssl_expires_at": null,
    "heartbeat_token": null,
    "heartbeat_url": null,
    "open_incident": null,
    "notification_contacts": [
      {
        "id": 9312,
        "name": "Ops email",
        "type": "email",
        "config": {
          "email": "ops@example.com"
        },
        "notify_on_down": true,
        "notify_on_recovery": true
      }
    ],
    "created_at": "2026-09-23T10:02:41.000000Z"
  }
}
```

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 404

```json
{"type": "about:blank", "title": "Not Found", "status": 404}
```

#### 422

```json
{
  "type": "https://monitor.agilepixel.io/docs/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 422,
  "detail": "The url field is required unless type is in heartbeat.",
  "errors": {
    "url": ["The url field is required unless type is in heartbeat."]
  }
}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```

## Delete a monitor
`DELETE /api/v1/monitors/{monitor}`

Stops checking the monitor and removes it from the team. Its history is kept, and an administrator can restore it. A heartbeat monitor's ping URL stops working.

| | |
|---|---|
| Authentication | Bearer token. Owners and Admins; Viewers get 403 |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `monitor` | path | integer | Yes | The monitor's ID. |

### Example request

```bash
curl -X DELETE https://monitor.agilepixel.io/api/v1/monitors/48213 \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Accept: application/json"
```

### Responses

| Status | When | Body |
|---|---|---|
| `204` | The monitor was deleted. There is no body. | None |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `404` | Nothing with that ID belongs to the team, or the path doesn't exist. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 204

No body.

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 404

```json
{"type": "about:blank", "title": "Not Found", "status": 404}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```

## Pause or resume a monitor
`POST /api/v1/monitors/{monitor}/toggle`

Flips `is_active`. Pausing stops checks and resolves the open incident; a paused heartbeat monitor answers its pings with 404. Resuming sets the status to `pending` until the next check.

| | |
|---|---|
| Authentication | Bearer token. Owners and Admins; Viewers get 403 |
| Rate limit | 60 requests per minute per user, shared by all of that user's tokens |

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `monitor` | path | integer | Yes | The monitor's ID. |

### Example request

```bash
curl -X POST https://monitor.agilepixel.io/api/v1/monitors/48213/toggle \
  -H "Authorization: Bearer $AGILEMONITOR_TOKEN" \
  -H "Accept: application/json"
```

### Responses

| Status | When | Body |
|---|---|---|
| `200` | The monitor after the change. | The [monitor](#monitor-object), in `data` |
| `401` | There is no token, or it is invalid or revoked. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `403` | The token is valid but may not do this: a Viewer tried to change something, the token isn't pinned to a team, or its creator has left the team. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `404` | Nothing with that ID belongs to the team, or the path doesn't exist. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `429` | The rate limit is used up. Wait for `Retry-After` seconds. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |
| `500` | Something went wrong on our side. Try again; if it persists, contact support. | [Problem](https://monitor.agilepixel.io/docs/api/errors) |

#### 200

```json
{
  "data": {
    "id": 48213,
    "name": "Storefront",
    "type": "http",
    "url": "https://shop.example.com",
    "status": "pending",
    "is_active": false,
    "check_interval_minutes": 5,
    "check_interval_seconds": null,
    "http_method": "GET",
    "expected_status_codes": [200],
    "expected_keyword": null,
    "port": null,
    "request_timeout_seconds": 30,
    "request_headers": null,
    "username": null,
    "has_password": false,
    "ssl_expiry_warning_days": 14,
    "last_checked_at": "2026-10-07T09:15:00.000000Z",
    "ssl_days_remaining": null,
    "ssl_expires_at": null,
    "heartbeat_token": null,
    "heartbeat_url": null,
    "open_incident": null,
    "notification_contacts": [],
    "created_at": "2026-09-23T10:02:41.000000Z"
  }
}
```

#### 401

```json
{"type": "about:blank", "title": "Unauthorized", "status": 401}
```

#### 403

```json
{"type": "about:blank", "title": "Forbidden", "status": 403}
```

#### 404

```json
{"type": "about:blank", "title": "Not Found", "status": 404}
```

#### 429

```json
{"type": "about:blank", "title": "Too Many Requests", "status": 429}
```

#### 500

```json
{"type": "about:blank", "title": "Internal Server Error", "status": 500}
```
