# API overview

The EvoHub API lets scripts, CI jobs and other systems read and change what is in your organization: alerts, schedules, monitors, status pages and more. This page covers what every request has in common.

## Base URL

All endpoints live under one base URL:

```
https://evohub.io/api/v1
```

Every request must use HTTPS. Paths in these docs are relative to the base URL, so `GET /monitors` means `GET https://evohub.io/api/v1/monitors`.

> [!NOTE]
> Sending alerts *into* EvoHub from a monitoring tool does not use the API key flow described here. Each On-Call integration has its own URL with its own credential. See [Generic webhook](https://docs-dev.evohub.io/generic-webhook.md).

## Authentication

Authenticate with an **API key**. Create one in the console under **My Account → API Keys**, or as an organization key under **Organization → Organization Keys**. See [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md).

Send the key in either of these headers. They are equivalent; use one.

:::code-group
```bash [Authorization header]
curl https://evohub.io/api/v1/monitors \
  -H "Authorization: Bearer evohub_YOUR_KEY"
```

```bash [X-API-Key header]
curl https://evohub.io/api/v1/monitors \
  -H "X-API-Key: evohub_YOUR_KEY"
```
:::

- Every EvoHub API key starts with `evohub_`.
- A key works in exactly one organization, so you never pass an organization ID.
- If you send both headers, they must carry the same key, or the request is refused.
- A missing, unknown, revoked or expired key gets **401 Unauthorized**. A valid key that lacks the scope for an endpoint gets **403 Forbidden**. See [Errors](https://docs-dev.evohub.io/errors.md).

## A first request

List your organization's uptime monitors. The key needs the `uptime:monitor:read` scope.

```bash
curl https://evohub.io/api/v1/monitors \
  -H "Authorization: Bearer $EVOHUB_API_KEY"
```

```json
{
  "data": [
    {
      "id": "…",
      "name": "Marketing site",
      "url": "https://www.acme.example",
      "type": "http",
      "interval_seconds": 60,
      "is_active": true,
      "last_status": "up",
      "last_checked_at": "2026-10-09T08:15:00Z",
      "created_at": "2026-09-01T10:00:00Z",
      "updated_at": "2026-10-01T12:30:00Z"
    }
  ]
}
```

The example shows a subset of the fields a monitor returns.

## Requests and responses

- Send request bodies as JSON with `Content-Type: application/json`.
- Successful responses are JSON with the result in a `data` field. Many endpoints also include `"success": true`.
- Errors are JSON with an `error` object instead. See [Errors](https://docs-dev.evohub.io/errors.md).
- Timestamps are RFC 3339 strings in UTC, for example `2026-10-09T08:15:00Z`. Send timestamps in the same format.
- IDs are opaque strings. Store them as they are; do not parse them.

## Pagination

Most list endpoints return the whole list. Where a list can grow large, it is paged with `limit` and `offset` query parameters, and the response carries the total number of matches in `meta.total` next to `data`.

For example, the On-Call alert list returns 50 alerts by default. The key needs the `oncall:alert:read` scope.

```bash
curl "https://evohub.io/api/v1/alerts?status=triggered&limit=20&offset=0" \
  -H "Authorization: Bearer $EVOHUB_API_KEY"
```

```json
{
  "data": [
    {
      "id": "…",
      "title": "High error rate on checkout",
      "severity": "critical",
      "status": "triggered",
      "created_at": "2026-10-09T08:01:12Z"
    }
  ],
  "success": true,
  "meta": { "total": 3 }
}
```

To read the next page, add `limit` to `offset` and request again until you have `meta.total` items.

## Request IDs

Every response carries an `X-Request-ID` header, and most error bodies repeat it as `request_id`. Include it when you contact support about a request; it lets us find exactly that call. You can also send your own `X-Request-ID` header, and EvoHub uses it instead of generating one.

## Rate limits

Requests are rate limited per API key (or, for requests without a key, per IP address), counted over a 10-second window. The limit is generous, on the order of 100 requests per second, and is meant to stop runaway scripts, not normal automation.

When you go over it, EvoHub answers **429 Too Many Requests** with a `RATE_LIMITED` error and a `Retry-After: 10` header. Wait at least that many seconds before retrying, and back off further if it happens again. Responses also carry `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers.

Alert ingest URLs and heartbeat ping URLs are not subject to this limit, so a burst of alerts during an outage is never dropped for being too many.

## Related

- [API keys and scopes](https://docs-dev.evohub.io/api-keys-and-scopes.md)
- [Errors](https://docs-dev.evohub.io/errors.md)
- [Roles and permissions](https://docs-dev.evohub.io/roles-and-permissions.md)
