# Authentication

Every Layer REST API request must be authenticated with a **bearer token** in the `Authorization` header:

```bash
curl https://api.app.layer.ai/api/v2/workspaces \
  -H "Authorization: Bearer $LAYER_TOKEN"
```

The API base URL is `https://api.app.layer.ai/api`, and every endpoint is versioned — `v2` is current, `v1` is still live — so full paths look like `https://api.app.layer.ai/api/v2/workspaces/{workspace_id}/...`. See [Versioning](/docs/versioning).

## Personal Access Tokens

A **Personal Access Token (PAT)** is the recommended way to authenticate a server-side integration. Tokens are prefixed with `pat_`.

1. Log in to [app.layer.ai](https://app.layer.ai).
2. Go to **Settings → Personal Access Tokens**.
3. Click **Create Token**, give it a name (and an optional expiry), and copy the value.

Caution

The token value is shown **only once**, at creation time — Layer stores a hash, not the token. Store it in a secret manager; if you lose it, create a new one.

A PAT is created and managed **only in the app UI** — there is no REST endpoint to mint, list, or revoke tokens. Creating one is free and needs no approval; see [self-serve access](/docs/self-serve-access).

### Token permissions

A PAT inherits **all** of the permissions of the user who created it — there are no per-token scopes. Treat a PAT as equivalent to that user’s access, and provision integration users with only the workspace access they need. See [Permissions](#permissions) for the full model.

### Expiry

If you set an expiry when creating the token, requests made after that time fail with [401 UNAUTHENTICATED](/docs/errors). Rotate tokens before they expire.

## OAuth 2.0

Layer runs an OAuth 2.0 authorization server at `https://auth.app.layer.ai`. It is what [MCP clients](/docs/mcp) connect through, and the access token it issues is accepted by the REST API as a bearer token too.

1. Discover the endpoints from the [authorization server metadata](https://auth.app.layer.ai/.well-known/oauth-authorization-server) ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)).
2. Register your client dynamically at the `registration_endpoint` — there is no pre-shared client id to request.
3. Run the **authorization code flow with PKCE** (`S256` required).

|                             |                                                                                                             |
| --------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Issuer                      | https://auth.app.layer.ai/                                                                                  |
| Metadata                    | [/.well-known/oauth-authorization-server](https://auth.app.layer.ai/.well-known/oauth-authorization-server) |
| Grants                      | authorization\_code, refresh\_token                                                                         |
| Scopes issued               | openid, profile, email, offline\_access                                                                     |
| PKCE                        | Required (S256)                                                                                             |
| Dynamic client registration | Supported                                                                                                   |

`layer.ai/.well-known/oauth-authorization-server` redirects to the document above — the metadata has to be served by the issuer itself, so the marketing domain points at it rather than mirroring it.

### Scopes

The scopes above are **OpenID Connect scopes**: they govern what the token tells Layer about _who you are_, not what you may do with the API. Layer does not define `read:*` / `write:*` API scopes — see [Permissions](#permissions).

There is no **client credentials** grant. Every token acts as a Layer user; for an unattended integration, create a Layer user for it and issue that user a PAT.

## Permissions

Layer authorizes each request from the **calling user**, not from the credential. Access is evaluated against that user’s role in the target workspace (`VIEWER`, `MEMBER`, `ADMIN`, `OWNER`) and, for project-scoped resources, their project membership. A request the user may not perform returns [403 FORBIDDEN](/docs/errors).

Two consequences worth designing around:

* **A credential cannot be narrowed below its owner.** A PAT inherits _all_ of its creating user’s permissions, and an OAuth access token acts as the user who authorized it.
* **Least privilege means a least-privileged user.** To restrict an integration, provision a dedicated Layer user with only the workspace role and project membership it needs, then create the credential as that user.

As a rule: `GET` operations need `VIEWER`; anything that creates, changes, or deletes state — or starts a run, and so spends Creative Units — needs at least `MEMBER`; workspace and member administration needs `ADMIN` or `OWNER`. The same rule is published machine-readably at [/api/index.json](https://layer.ai/api/index.json) under `interfaces.rest.permissions`.

Note

The [MCP servers](/docs/mcp) use the OAuth flow above; their setup guides walk through it per client. The [Management MCP server](/docs/mcp/management) is a separate endpoint so administrative tools stay out of the creative toolset — but both act as your user and respect the same workspace roles.

## Keeping tokens safe

* Never embed a PAT in client-side code or a public repository — the API is [server-to-server only](/docs/integration-notes).
* Scope integration users to the minimum workspace access required.
* Rotate tokens periodically and on suspected exposure.
