# Status API

The Status API lets scripts and CI jobs do what the Status Pages console does: create pages and components, set a component's status by hand, post incidents and scheduled maintenance with their updates, look after subscribers, connect a custom domain and read visitor analytics. Every endpoint, with its parameters, schemas, errors and an example request, is in the [Status API reference](https://docs-dev.evohub.io/status.md). This page shows the common tasks and the rules that apply to all of them.

Paths below are relative to `https://evohub.io`. Requests need an API key, sent as described in [API overview](https://docs-dev.evohub.io/api-overview.md#authentication), except the public page reads at the end.

> [!NOTE]
> In the console, incidents and maintenance on a status page are run from On-Call, which publishes them for you. Use the [On-Call API](https://docs-dev.evohub.io/on-call-api.md) for that flow. The endpoints here write to the page directly, for teams that run their status page on its own.

## Scopes

Each endpoint needs one scope. Give a key only the ones its job needs.

| To | Scope |
| --- | --- |
| List and read pages, their domain, logos and analytics | `status:page:read` |
| Create, change and delete pages; theme, header, layout, logos, domain, analytics settings | `status:page:write` |
| Read components | `status:component:read` |
| Add, change, reorder, rename groups of, set the status of and delete components | `status:component:write` |
| Read incidents | `status:incident:read` |
| Create incidents, post and edit their updates, write postmortems | `status:incident:write` |
| Read maintenance | `status:maintenance:read` |
| Schedule maintenance and post its updates | `status:maintenance:write` |
| Read subscribers | `status:subscriber:read` |
| Remove subscribers, change subscription settings | `status:subscriber:write` |
| Read the Status audit log | `status:audit:read` |

A key without the scope gets **403**, never 401. See [Errors](https://docs-dev.evohub.io/errors.md#401-versus-403).

## Set a component's status

```bash
curl -X PATCH https://evohub.io/api/v1/components/$COMPONENT_ID/status \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"current_status": "degraded"}'
```

The status is one of `operational`, `degraded`, `partial_outage`, `major_outage` and `maintenance`. A component linked to an Uptime monitor (`source: uptime_monitor`) keeps showing its monitor's live state, whatever you set. List a page's components, with their ids, with `GET /api/v1/pages/{pageID}/components`.

## Post an incident

```bash
curl -X POST https://evohub.io/api/v1/pages/$PAGE_ID/incidents \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "Elevated API error rates",
        "impact": "major",
        "affected_component_ids": ["'"$COMPONENT_ID"'"],
        "body": "We are looking into elevated error rates on the API.",
        "managed_in": "status_page"
      }'
```

`impact` is `none`, `minor`, `major` or `critical`; it decides how the affected components show while the incident is open. With a `body`, confirmed subscribers who follow an affected component, or the whole page, get an email.

Then keep the timeline going and close it:

```bash
curl -X POST https://evohub.io/api/v1/pages/$PAGE_ID/incidents/$INCIDENT_ID/updates \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved", "body": "Error rates are back to normal."}'
```

Each update moves the incident to its `status` (`investigating`, `identified`, `monitoring` or `resolved`) and mails subscribers. Editing the incident with `PATCH` changes its title, impact or status without an update and without mail. After it is resolved, publish a postmortem with `PUT …/incidents/{id}/postmortem` and `{"body": "…", "published": true}`.

To record an incident that is already over, send `started_at`, `resolved_at` and `resolution_body` when you create it. It lands on the days it happened and mails nobody.

## Schedule maintenance

```bash
curl -X POST https://evohub.io/api/v1/pages/$PAGE_ID/maintenances \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "title": "Database upgrade",
        "body": "Expect up to five minutes of read-only mode.",
        "scheduled_start": "2026-10-12T22:00:00Z",
        "scheduled_end": "2026-10-13T00:00:00Z",
        "affected_component_ids": ["'"$COMPONENT_ID"'"],
        "managed_in": "status_page"
      }'
```

Post `in_progress` and `completed` updates to `…/maintenances/{id}/updates` as the work goes. Setting the window to `completed` with `PATCH` also closes its timeline with "This maintenance has been completed."

## Connect a custom domain

```bash
curl -X POST https://evohub.io/api/v1/pages/$PAGE_ID/domain \
  -H "Authorization: Bearer $EVOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": "status.example.com"}'
```

Use a subdomain; an apex domain such as `example.com` is refused. Create the CNAME records the answer lists at your DNS provider, then poll `GET /api/v1/pages/{pageID}/domain` until `status` is `active`. See [Custom domain](https://docs-dev.evohub.io/status-page-custom-domain.md).

## Read visitor analytics

`GET /api/v1/pages/{pageID}/analytics?from=2026-09-01&to=2026-09-30` returns views, visitors, feed and AI reads per day and the top routes, referrers, countries and devices. `GET /api/v1/pages/analytics` does the same across every page the key can see, with subscriber growth and incidents published. Download one report as CSV from `…/analytics/export.csv?report=referrers`.

## Rules that apply everywhere

- **Teams.** A page belongs to the whole organization or to one team. A key sees the organization-wide pages; an organization key scoped to a team also sees that team's. A page the key cannot see, and everything under it, answers **404**, as if it did not exist.
- **Strict bodies.** A field the endpoint does not take is refused with **400** `INVALID_BODY`, so a typo cannot be silently ignored.
- **Updates.** A field you leave out keeps its value, on `PUT` of a page as on every `PATCH`. `null` clears a nullable field. A page's `slug` and `team_id` do not change through `PUT`: a different value is refused with `VALIDATION_ERROR`.
- **Errors.** This service answers a bad field with `VALIDATION_ERROR` and the field in the message, and an unexpected failure with `INTERNAL_ERROR`.
- **Public reads.** `GET /api/v1/public/pages/{pageID}` (or `/by-domain/{domain}`) and their `feed.rss` and `feed.atom` need no key and return only what visitors see. A private page answers 404.

## Related

- [Status API reference](https://docs-dev.evohub.io/status.md)
- [On-Call API](https://docs-dev.evohub.io/on-call-api.md)
- [Status pages overview](https://docs-dev.evohub.io/status-pages-overview.md)
- [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md)
- [Errors](https://docs-dev.evohub.io/errors.md)
