> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sitepulse.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Endpoints

> Complete v1 HTTP reference for all API routes

All paths are prefixed with `/api/v1`. Unless noted, requests require:

* `Authorization: Bearer {token}`
* `Accept: application/json`
* Verified user email
* Appropriate [OAuth scope](/api/authentication#oauth-scopes)

Team-scoped routes also require [active team subscription](/api/authentication#team-access-and-billing).

## Me

### GET /me

Returns the authenticated user and all teams they belong to.

**Scope:** `teams:read`

**Response `200`:**

```json theme={null}
{
  "data": {
    "user": { "name": "...", "email": "...", "email_verified_at": "..." },
    "teams": [ /* Team resources with limits */ ]
  }
}
```

## Teams

### GET /teams

List teams for the authenticated user.

**Scope:** `teams:read`\
**Team permission:** `viewAny` on teams

**Response `200`:** `{ "data": [ Team, ... ] }`

### GET /teams/\{team}

Show one team by UUID.

**Scope:** `teams:read`\
**Team permission:** `view` on team

**Response `200`:** Team resource

### GET /teams/\{team}/monitoring

Read team monitoring defaults: enabled tools, cadences, request header, issue settings, quiet time, escalation, cadence presets, and UI helper metadata.

**Scope:** `teams:read`\
**Team permission:** `view` team

**Response `200`:**

```json theme={null}
{
  "data": {
    "settings": {
      "tools": {
        "status": {
          "enabled": true,
          "cadence_minutes": 5,
          "settings": {
            "incident_status_codes": [400, 401, 403, 404, 405, 408, 410, 429, 500, 501, 502, 503, 504, 521, 522, 523, 524],
            "failures_to_open": 2
          }
        }
      },
      "request_header": { "name": null, "value": null },
      "quiet_time": { "enabled": false, "starts_at": "22:00", "ends_at": "07:00" },
      "escalation": { "enabled": false, "intervals": [] }
    },
    "cadence_presets": [{ "minutes": 5, "label": "5 minutes", "disabled": false }],
    "minimum_check_cadence_minutes": 5,
    "allowed_tools": [{ "value": "status", "label": "Status", "minimum_check_cadence_minutes": 5 }],
    "failure_threshold_presets": [{ "key": "instant", "label": "Instant", "failures_to_open": 1 }],
    "escalation_interval_presets": [{ "key": "15m", "label": "15 minutes", "minutes": 15 }],
    "dns_record_types": ["A", "AAAA", "CNAME", "MX", "NS", "TXT"],
    "status_incident_code_options": [
      { "code": 400, "label": "Bad Request", "group": "client" },
      { "code": 401, "label": "Unauthorized", "group": "client" },
      { "code": 403, "label": "Forbidden", "group": "client" },
      { "code": 404, "label": "Not Found", "group": "client" },
      { "code": 405, "label": "Method Not Allowed", "group": "client" },
      { "code": 408, "label": "Request Timeout", "group": "client" },
      { "code": 410, "label": "Gone", "group": "client" },
      { "code": 429, "label": "Too Many Requests", "group": "client" },
      { "code": 500, "label": "Internal Server Error", "group": "server" },
      { "code": 501, "label": "Not Implemented", "group": "server" },
      { "code": 502, "label": "Bad Gateway", "group": "server" },
      { "code": 503, "label": "Service Unavailable", "group": "server" },
      { "code": 504, "label": "Gateway Timeout", "group": "server" },
      { "code": 521, "label": "Web Server Is Down", "group": "server" },
      { "code": 522, "label": "Connection Timed Out", "group": "server" },
      { "code": 523, "label": "Origin Is Unreachable", "group": "server" },
      { "code": 524, "label": "A Timeout Occurred", "group": "server" }
    ]
  }
}
```

See [Team monitoring resource](/api/resources#team-monitoring) for field details.

### PATCH /teams/\{team}/monitoring

Update team monitoring defaults. Mirrors the dashboard **Monitoring** page (automatic checks, issue rules, escalation, and quiet time).

**Scope:** `teams:write`\
**Team permission:** `updateMonitoring` (admins and owners)\
**Response `200`:** Same shape as `GET /teams/{team}/monitoring`

**Body:**

| Field                                              | Required | Description                                                          |
| -------------------------------------------------- | -------- | -------------------------------------------------------------------- |
| `monitoring_settings`                              | yes      | Full team monitoring settings object                                 |
| `monitoring_settings.tools`                        | yes      | Map of tool value → tool settings                                    |
| `monitoring_settings.tools.{tool}.enabled`         | yes      | Whether the tool runs automatically and can open issues              |
| `monitoring_settings.tools.{tool}.cadence_minutes` | yes      | Cadence preset in minutes                                            |
| `monitoring_settings.tools.{tool}.settings`        | no       | Tool-specific issue settings                                         |
| `monitoring_settings.request_header`               | no       | One HTTP header sent by Status, Broken Links, and Performance checks |
| `monitoring_settings.quiet_time`                   | no       | `{ "enabled", "starts_at", "ends_at" }` in `H:i` format              |
| `monitoring_settings.escalation`                   | no       | `{ "enabled", "intervals" }` follow-up notification schedule         |

Tool-specific fields match [the monitoring settings schema](#monitoring-settings-fields).

<Warning>
  `monitoring_settings.tools` must include the tools you want configured. Dashboard updates send the full settings object.
</Warning>

<Note>
  DNS **expected records** are not writable through this endpoint. Configure them on a site's edit page or with `dns_expected_records` on `POST /teams/{team}/sites` or `PATCH /sites/{site}`.
</Note>

**Example:**

```json theme={null}
{
  "monitoring_settings": {
    "tools": {
      "status": {
        "enabled": true,
        "cadence_minutes": 60,
        "settings": {
          "incident_status_codes": [404, 500, 502, 503, 504],
          "failures_to_open": 1,
          "successes_to_resolve": 1,
          "confirmation_retry_minutes": 2,
          "incident_follow_up_minutes": 5
        }
      },
      "ssl": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "expiry_days": 14,
          "failures_to_open": 1,
          "confirmation_retry_minutes": 2
        }
      },
      "dns": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "hard_failures_only": true,
          "monitored_record_types": ["A", "TXT"]
        }
      },
      "broken-links": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "min_broken_count": 4,
          "consecutive_breaches_to_open": 1,
          "ignore_paths": ["/wp-admin/*"],
          "ignore_external_links": true,
          "ignore_redirects": true
        }
      },
      "performance": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "min_score": 60,
          "max_response_time_ms": 3000,
          "max_ttfb_ms": 3000,
          "max_load_time_seconds": 5,
          "consecutive_breaches_to_open": 2
        }
      }
    },
    "request_header": {
      "name": "X-Monitoring-Token",
      "value": "secret"
    },
    "quiet_time": {
      "enabled": false,
      "starts_at": "22:00",
      "ends_at": "07:00"
    },
    "escalation": {
      "enabled": true,
      "intervals": [60]
    }
  }
}
```

<Note>
  Updating cadences resyncs automatic schedules for all team sites.
</Note>

<h3 id="monitoring-settings-fields">
  Monitoring settings fields
</h3>

The same nested shape is used for team monitoring updates and per-site overrides.

| Field                                                                                                                                 | Type            | Limits                                  | Description                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------- | --------------- | --------------------------------------- | -------------------------------------------------------------------------------- |
| `request_header.name`                                                                                                                 | string \| null  | valid HTTP header name, max 255 chars   | Header name sent by Status, Broken Links, and Performance checks                 |
| `request_header.value`                                                                                                                | string \| null  | max 2,000 chars, no newlines            | Header value sent by Status, Broken Links, and Performance checks                |
| `tools.status.enabled`                                                                                                                | boolean         | -                                       | Run Status automatically and allow status issues                                 |
| `tools.status.cadence_minutes`                                                                                                        | integer         | cadence preset                          | Automatic Status cadence                                                         |
| `tools.status.settings.incident_status_codes`                                                                                         | integer\[]      | supported status-code options           | Response codes that count as failed status checks; request failures always count |
| `tools.status.settings.failures_to_open`                                                                                              | integer         | min 1                                   | Failed status checks before opening an issue                                     |
| `tools.status.settings.successes_to_resolve`                                                                                          | integer         | min 1                                   | Successful checks before resolving                                               |
| `tools.status.settings.confirmation_retry_minutes`                                                                                    | integer         | 1-5                                     | Confirmation retry delay                                                         |
| `tools.status.settings.incident_follow_up_minutes`                                                                                    | integer         | 1-60                                    | Active incident follow-up interval                                               |
| `tools.ssl.enabled`                                                                                                                   | boolean         | -                                       | Run SSL automatically and allow SSL issues                                       |
| `tools.ssl.cadence_minutes`                                                                                                           | integer         | cadence preset                          | Automatic SSL cadence                                                            |
| `tools.ssl.settings.expiry_days`                                                                                                      | integer         | min 1                                   | Days before expiry to alert                                                      |
| `tools.ssl.settings.failures_to_open`                                                                                                 | integer         | min 1                                   | Failed SSL checks before opening an issue                                        |
| `tools.ssl.settings.confirmation_retry_minutes`                                                                                       | integer         | 1-5                                     | Confirmation retry delay                                                         |
| `tools.broken-links.enabled`                                                                                                          | boolean         | -                                       | Run Broken Links automatically and allow broken-link issues                      |
| `tools.broken-links.cadence_minutes`                                                                                                  | integer         | cadence preset, 12-hour floor           | Automatic Broken Links cadence                                                   |
| `tools.broken-links.settings.min_broken_count`                                                                                        | integer         | min 1                                   | Broken URLs to breach                                                            |
| `tools.broken-links.settings.consecutive_breaches_to_open`                                                                            | integer         | min 1                                   | Consecutive breaches before opening                                              |
| `tools.broken-links.settings.ignore_paths`                                                                                            | string\[]       | max 255 chars each                      | Crawl exclusion path patterns                                                    |
| `tools.broken-links.settings.ignore_external_links`                                                                                   | boolean         | -                                       | Exclude links outside the site host                                              |
| `tools.broken-links.settings.ignore_redirects`                                                                                        | boolean         | -                                       | Exclude redirect responses                                                       |
| `tools.performance.enabled`                                                                                                           | boolean         | -                                       | Run Performance automatically and allow performance issues                       |
| `tools.performance.cadence_minutes`                                                                                                   | integer         | cadence preset, 12-hour floor           | Automatic Performance cadence                                                    |
| `tools.performance.settings.url`                                                                                                      | string \| null  | same site domain                        | Page URL to audit instead of the site root                                       |
| `tools.performance.settings.min_score`                                                                                                | integer         | 1-100                                   | Minimum performance score                                                        |
| `tools.performance.settings.max_response_time_ms`                                                                                     | integer \| null | min 1                                   | Legacy max response time, normalized with `max_ttfb_ms`                          |
| `tools.performance.settings.max_ttfb_ms`                                                                                              | integer \| null | min 1                                   | Max time to first byte                                                           |
| `tools.performance.settings.max_load_time_seconds`                                                                                    | integer \| null | min 1                                   | Max Time to Interactive                                                          |
| `tools.performance.settings.consecutive_breaches_to_open`                                                                             | integer         | min 1                                   | Consecutive breaches before opening                                              |
| `tools.dns.enabled`                                                                                                                   | boolean         | -                                       | Run DNS automatically and allow DNS issues                                       |
| `tools.dns.cadence_minutes`                                                                                                           | integer         | cadence preset                          | Automatic DNS cadence                                                            |
| `tools.dns.settings.hard_failures_only`                                                                                               | boolean         | -                                       | Open only on lookup failures when true                                           |
| `tools.dns.settings.monitored_record_types`                                                                                           | string\[]       | `A`, `AAAA`, `CNAME`, `MX`, `NS`, `TXT` | Record types to monitor for changes                                              |
| `quiet_time.enabled`                                                                                                                  | boolean         | team only                               | Suppress outbound issue alerts during a daily window                             |
| `quiet_time.starts_at`                                                                                                                | string          | `H:i`                                   | Quiet time start                                                                 |
| `quiet_time.ends_at`                                                                                                                  | string          | `H:i`, different from start             | Quiet time end                                                                   |
| `escalation.enabled`                                                                                                                  | boolean         | team only                               | Send reminders for active issues                                                 |
| `escalation.intervals`                                                                                                                | integer\[]      | `0`, `15`, `30`, `60`, `360`, `1440`    | Reminder schedule in minutes (`0` sends immediately)                             |
| For broken-link crawls, Sitepulse sends `request_header` only to URLs on the monitored site's host. External links do not receive it. |                 |                                         |                                                                                  |

At site level, provide both `request_header.name` and `request_header.value` to replace the team header. Omit the object or leave it empty to inherit the team value.

## Sites

Team-scoped site management uses the team UUID as `{team}`. Individual site routes use `{site}` uuid.

### GET /teams/\{team}/sites

Paginated site list for a team. Includes latest check per tool.

**Scope:** `sites:read`\
**Team permission:** `viewAny` sites

**Query parameters:**

| Parameter  | Type    | Description                                                     |
| ---------- | ------- | --------------------------------------------------------------- |
| `search`   | string  | Filter by name or URL (partial match)                           |
| `pinned`   | boolean | `true` = pinned only; `false` = unpinned only                   |
| `tool`     | string  | Sites with this tool enabled                                    |
| `status`   | string  | Sites with a check in this status (`pending`, `completed`, ...) |
| `sort`     | string  | `created_at`, `-created_at`, `name`, `-name`, `pinned`          |
| `page`     | integer | Page number                                                     |
| `per_page` | integer | Page size (max 100)                                             |

**Response `200`:** Paginated [Site](/api/resources#site) collection

### POST /teams/\{team}/sites

Create a site.

**Scope:** `sites:write`\
**Team permission:** `create` site\
**Response:** `201` with Site resource

**Body:**

| Field                  | Required | Description                                                                                       |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `url`                  | yes      | Valid HTTP(S) URL (normalized)                                                                    |
| `name`                 | no       | Display name; generated from domain if omitted                                                    |
| `notes`                | no       | Team notes                                                                                        |
| `js_rendered`          | no       | Boolean; broken-link JS rendering                                                                 |
| `monitoring_settings`  | no       | Per-site monitoring overrides using [the monitoring settings schema](#monitoring-settings-fields) |
| `dns_expected_records` | no       | Per-site expected DNS records by type. Omit to use the first DNS check as the baseline.           |

<Note>
  The API accepts omitted `name`. The dashboard add-site form requires a display name and exposes detailed overrides on the site edit page after creation.
</Note>

<Warning>
  Plan limits apply to site count and automatic monitoring cadence.
</Warning>

**Example:**

```json theme={null}
{
  "url": "https://example.com",
  "name": "Example",
  "dns_expected_records": {
    "A": ["203.0.113.1"]
  },
  "monitoring_settings": {
    "tools": {
      "status": {
        "enabled": true,
        "cadence_minutes": 60,
        "settings": {
          "incident_status_codes": [404, 500, 502, 503, 504]
        }
      },
      "ssl": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": { "expiry_days": 14 }
      }
    },
    "request_header": {
      "name": "X-Monitoring-Token",
      "value": "secret"
    }
  }
}
```

### POST /teams/\{team}/sites/bulk

Create up to 100 sites in one request.

**Scope:** `sites:write`\
**Team permission:** `create` site\
**Response `200`:** [Bulk response](/api/resources#bulk-site-create-response)

**Body:**

```json theme={null}
{
  "sites": [
    { "url": "https://a.example.com", "name": "A" },
    { "url": "https://b.example.com" }
  ]
}
```

### GET /sites/\{site}

Show one site by uuid.

**Scope:** `sites:read`\
**Team permission:** `view` site

**Response `200`:** Site resource with `latest_checks`

### PATCH /sites/\{site}

Update a site. Sends the full site shape expected by the dashboard update form (including `url`).

**Scope:** `sites:write`\
**Team permission:** `update` site\
**Response `200`:** Updated Site resource

**Body:** `url` is required on every request. The other fields are optional.

| Field                  | Description                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| `url`                  | Canonical URL. Required by the current validator.                                                 |
| `name`                 | Display name                                                                                      |
| `notes`                | Notes                                                                                             |
| `pinned`               | Boolean pin state                                                                                 |
| `js_rendered`          | Broken-link JS mode                                                                               |
| `monitoring_settings`  | Per-site monitoring overrides using [the monitoring settings schema](#monitoring-settings-fields) |
| `dns_expected_records` | Per-site expected DNS records by type. Send `{}` or `null` to clear the baseline.                 |

**Example:**

```json theme={null}
{
  "url": "https://example.com",
  "dns_expected_records": {
    "A": ["203.0.113.1"]
  },
  "monitoring_settings": {
    "tools": {
      "status": {
        "enabled": true,
        "cadence_minutes": 60,
        "settings": {
          "incident_status_codes": [404, 500, 502, 503, 504],
          "failures_to_open": 1,
          "successes_to_resolve": 1
        }
      },
      "broken-links": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "ignore_paths": ["/admin", "/preview"]
        }
      },
      "dns": {
        "enabled": true,
        "cadence_minutes": 1440,
        "settings": {
          "hard_failures_only": false,
          "monitored_record_types": ["A", "TXT"]
        }
      }
    },
    "request_header": {
      "name": "X-Monitoring-Token",
      "value": "site-secret"
    }
  }
}
```

<Note>
  Updating enabled checks or cadences resyncs automatic schedules.
</Note>

### DELETE /sites/\{site}

Delete a site and its schedules, checks, and issues.

**Scope:** `sites:delete`\
**Team permission:** `delete` site\
**Response:** `204 No Content`

## Checks

### GET /sites/\{site}/checks

Paginated check history for a site.

**Scope:** `checks:read`\
**Team permission:** `viewChecks`

**Query parameters:**

| Parameter          | Type    | Description                                             |
| ------------------ | ------- | ------------------------------------------------------- |
| `type`             | string  | Tool filter                                             |
| `status`           | string  | Check status                                            |
| `automatic`        | boolean | Automatic vs on-demand                                  |
| `run_reason`       | string  | `scheduled`, `confirmation_retry`, `incident_follow_up` |
| `from`             | date    | `created_at` ≥ date                                     |
| `to`               | date    | `created_at` ≤ date (must be ≥ `from`)                  |
| `page`, `per_page` | integer | Pagination                                              |

**Response `200`:** Paginated [Check](/api/resources#check) collection

### POST /sites/\{site}/checks

Queue one or more on-demand checks. Every check queued through the API is an on-demand check, counts against your team's monthly on-demand check quota, and is subject to API cadence protection.

**Scope:** `checks:run`\
**Team permission:** `createCheck`\
**Rate limit:** 10 requests / minute\
**Response:** `202 Accepted` with array of Check resources (usually `pending`)

**Body:**

| Field       | Required | Description                                                                                               |
| ----------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `tools`     | no       | Tool values to run; defaults to all **enabled** site tools                                                |
| `automatic` | no       | **Deprecated.** API runs are always on-demand. If sent, it must be `false`; sending `true` returns `422`. |

**Rules:**

* Each tool must be enabled on the site
* The tool must not already have a `pending` on-demand check on the site
* Through the API, the same tool can only be queued again after its minimum on-demand cadence has passed for the site
* Automatic checks do not block on-demand runs

**Example:**

```json theme={null}
{
  "tools": ["status", "ssl"]
}
```

**Validation errors (`422`):**

| `errors.tools[0]` message                                          | Cause                                                                                    |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| `Your current plan has reached its monthly on-demand check limit.` | The team has used its monthly on-demand check quota                                      |
| `This site already has a pending {tool} check.`                    | An on-demand check of that tool is still running on the site                             |
| `{Tool} checks can only be run every {cadence} for this site.`     | The tool was queued through the API too recently; wait for the minimum on-demand cadence |

<Info>
  Poll `GET /checks/{check}` until `status` is terminal. See [Check results](/api/check-results).
</Info>

### GET /checks/\{check}

Show one check by uuid, including full `result` and nested `site` summary.

**Scope:** `checks:read`\
**Team permission:** `view` check (via site team membership)

**Response `200`:** Check resource

### DELETE /checks/\{check}

Delete an **on-demand** check (`automatic: false`).

**Scope:** `checks:delete`\
**Team permission:** `deleteCheck`\
**Response:** `204` on success; `403` for automatic checks

## Reports

Read-only aggregates. Require `sites:read` and `view` on the site.

### GET /sites/\{site}/reports/uptime

Daily uptime percentages from automatic status checks.

**Query:** `days` (integer, default `90`, max `365`)

**Response `200`:** [Uptime report](/api/resources#uptime-report)

### GET /sites/\{site}/reports/performance

Daily performance aggregates from automatic performance checks.

**Query:** `days` (integer, default `30`, max `365`)

**Response `200`:** [Performance report](/api/resources#performance-report)

## Issues

Read-only access to issue records. The dashboard **Issues** page supports acknowledge, resolve, and bulk actions; those operations are not available through the API.

### GET /issues

Paginated list of issues across teams the user can view.

**Scope:** `issues:read`\
**Team permission:** `viewIssues` per team

**Query parameters:**

| Parameter          | Type    | Default  | Description                                                           |
| ------------------ | ------- | -------- | --------------------------------------------------------------------- |
| `team`             | string  | -        | Limit to one team UUID                                                |
| `status`           | string  | `active` | `active` (open + acknowledged), `open`, `acknowledged`, or `resolved` |
| `site`             | string  | -        | Site uuid                                                             |
| `tool`             | string  | -        | Check tool (`status`, `ssl`, `dns`, `broken-links`, `performance`)    |
| `severity`         | string  | -        | `critical`, `warning`, `info`                                         |
| `page`, `per_page` | integer |          | Pagination (max 100 per page)                                         |

**Response `200`:** Paginated collection of [Issue](/api/resources#issue) resources.

### GET /issues/\{issue}

Retrieve a single issue with related site, check, timeline, and acknowledgement/resolution metadata.

**Scope:** `issues:read`\
**Team permission:** `view` the issue's team

**Response `200`:** [Issue](/api/resources#issue) resource

## Endpoint summary

Quick reference table:

| Method   | Path                                | Scope           |
| -------- | ----------------------------------- | --------------- |
| `GET`    | `/me`                               | `teams:read`    |
| `GET`    | `/teams`                            | `teams:read`    |
| `GET`    | `/teams/{team}`                     | `teams:read`    |
| `GET`    | `/teams/{team}/monitoring`          | `teams:read`    |
| `PATCH`  | `/teams/{team}/monitoring`          | `teams:write`   |
| `GET`    | `/teams/{team}/sites`               | `sites:read`    |
| `POST`   | `/teams/{team}/sites`               | `sites:write`   |
| `POST`   | `/teams/{team}/sites/bulk`          | `sites:write`   |
| `GET`    | `/sites/{site}`                     | `sites:read`    |
| `PATCH`  | `/sites/{site}`                     | `sites:write`   |
| `DELETE` | `/sites/{site}`                     | `sites:delete`  |
| `GET`    | `/sites/{site}/checks`              | `checks:read`   |
| `POST`   | `/sites/{site}/checks`              | `checks:run`    |
| `GET`    | `/sites/{site}/reports/uptime`      | `sites:read`    |
| `GET`    | `/sites/{site}/reports/performance` | `sites:read`    |
| `GET`    | `/checks/{check}`                   | `checks:read`   |
| `DELETE` | `/checks/{check}`                   | `checks:delete` |
| `GET`    | `/issues`                           | `issues:read`   |
| `GET`    | `/issues/{issue}`                   | `issues:read`   |

## See also

* [API overview](/api/overview)
* [Authentication](/api/authentication)
* [Resource reference](/api/resources)
