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

# Introduction

> Base URL, credentials, the factory header, errors and rate limits for the OuterLayer API.

The OuterLayer API is what the `outerlayer` CLI and the dashboard call. Call it yourself to script what they do.

Every endpoint is under one base URL:

```text theme={"system"}
https://api.outerlayer.ai
```

Paths start with `/v1/`. A breaking change ships under a new prefix, such as `/v2/`.

## Authenticate

Send a credential as a bearer token on every request:

```text theme={"system"}
Authorization: Bearer <credential>
```

Which credential you send depends on the endpoint:

| Endpoints | Credential | Where you get it |
| - | - | - |
| Everything under `/v1/` except `/v1/orgs/...` and `/v1/me/factories` | A factory API key (`sk_outerlayer_...`) | The factory's **Settings → API keys**. See [API keys](/api-keys). |
| The same factory endpoints | A CLI login token (`olu_...`) | `outerlayer login` with nothing piped. It acts with your own permissions. |
| `/v1/orgs/{orgName}/...` | A management API key (`olk_...`) | The organization's **Settings → Management API keys**. See [Organization API](/organization-api). |
| `/v1/me/factories` | A CLI login token only | `outerlayer login`. |

A factory key never works on `/v1/orgs/...`. A management key never works on factory endpoints.

`GET /health`, `GET /v1/health/ingestion`, `GET /v1/health/files`, `GET /v1/capabilities`, `GET /v1/pricing` and `GET /.well-known/oauth-protected-resource` need no credential.

## Name the factory

Factory endpoints also need the `X-Outerlayer-App-Id` header. Its value is the factory id. Copy it from the factory's **Settings → General**.

```text theme={"system"}
X-Outerlayer-App-Id: <factory-id>
```

Without it, a factory key gets `401` and a login token gets `400 app_id_required`. A login token acts only on the factory this header names. A path that names another factory, such as `/v1/apps/{appId}`, answers `403`.

You can leave the header out on:

* `/v1/orgs/{orgName}/...` and `/v1/me/factories`, which name no factory.
* `/v1/mcp` with a factory key, because the key belongs to one factory.
* `/v1/apps/{appId}/mcp`, which takes the factory from the path. It accepts only a user or OAuth token, never an API key.

## Make a request

List the factory's live work items:

```bash theme={"system"}
curl https://api.outerlayer.ai/v1/work-items \
  -H "Authorization: Bearer <factory-api-key>" \
  -H "X-Outerlayer-App-Id: <factory-id>"
```

The key needs `work.read`. A page holds up to 100 items:

```json theme={"system"}
{
  "data": [
    {
      "id": "<work-item-uuid>",
      "number": 12,
      "tracker": "github",
      "trackerKey": "acme/api#42",
      "title": "Add retry to the webhook sender",
      "specRepository": "acme/api",
      "specNumber": 42,
      "stage": "build",
      "section": "flowing",
      "blockClass": "none",
      "claim": null,
      "startable": false
    }
  ],
  "pagination": { "cursor": null }
}
```

When `pagination.cursor` is not `null`, send it back as `?cursor=<cursor>` for the next page.

## Errors

An error answers with a status code and this body:

```json theme={"system"}
{ "error": { "code": "forbidden", "message": "Missing required permission 'work.read'", "required_permission": "work.read" } }
```

Branch on `error.code`, which is stable. `error.message` is for people. Some errors add fields, such as `required_permission` above.

| Status | Means | What to do |
| - | - | - |
| `400` | The request is malformed: a bad body, query, cursor or factory id. | Read `error.code` and `error.message`, fix the request, and send it again. |
| `401` | The credential is missing, unknown or revoked. Or `X-Outerlayer-App-Id` is missing, or names a factory the key does not belong to. | Check both headers. A deleted key is refused within about five minutes. |
| `402` | The plan does not include the feature, or a limit is reached. `error.code` is `entitlement_required`, and `error.entitlement` names it. | Upgrade the plan, or free up the limit, such as deleting an unused key. |
| `403` | The credential lacks the endpoint's permission, or acts outside what it may reach. A key whose creator has left the organization gets `403` too. | Add the permission named in `error.required_permission` to the key, or use a key that has it. |
| `404` | The resource does not exist in this factory or organization. | Check the id, and that the header names the right factory. |
| `409` | The request conflicts with the current state, such as a name already taken or a missing GitHub App installation. | Read `error.code`, resolve the conflict, then retry. |
| `410` | A work item's claim has expired. `error.code` is `claim_expired`. | Claim the item again. |
| `413` | The body is larger than the endpoint accepts. | Split the upload into smaller requests. |
| `422` | The request is well formed but cannot be carried out, such as a pull request from a fork. | Read `error.message`; it says what to change. |
| `426` | A runner is older than the gateway accepts. `error.code` is `runner_upgrade_required`. | Upgrade the CLI to at least `error.minimum`. `error.docs` links the guide. |
| `429` | A rate limit is reached. `error.code` is `rate_limited`. | Wait the seconds in `Retry-After`, then retry. |
| `5xx` | The gateway or a service behind it failed. `503` with `store_unavailable` means a data store did not answer. | Retry with backoff. If it persists, email [hello@outerlayer.ai](mailto:hello@outerlayer.ai) with the time and the path you called. |

## Rate limits

Most endpoints have no rate limit. These do, counted per organization:

| Endpoints | Free plan | Paid plans |
| - | - | - |
| `GET /v1/sessions`, `/v1/metrics/*`, `/v1/prs/outcomes`, `/v1/context/changes`, `/v1/context/source` | 100 a minute | 1,000 a minute |
| `GET /v1/sessions/{traceId}` | 30 a minute | 300 a minute |
| `/v1/mcp` protocol calls that read no data, such as `tools/list` | 300 a minute | 1,000 a minute |

An MCP tool call counts against the same limit as the REST endpoint it mirrors.

Each limit has its own bucket. A `429` carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and, when known, `X-RateLimit-Reset` (Unix time in milliseconds) and `Retry-After` (seconds).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.