# Authentication & API keys

> Bearer keys with action scopes, read-only keys, workflow and folder allowlists, rotation with a 24-hour grace period, and revocation — all self-serve.

Source: https://developers.processcart.com/guides/authentication

Every request to `/api/v1` (except `GET /v1/health`) carries an API key. Keys are created by an Admin in **Settings → API**, shown exactly once, and stored only as a hash — Process Cart cannot show a key again.

## Sending the key

The canonical form is a Bearer token:

```http
GET /api/v1/me HTTP/1.1
Host: app.processcart.com
Authorization: Bearer pc_live_8Jq3…
```

Tools that can only set a custom header may send `X-API-Key: pc_live_8Jq3…` instead. Sending both headers with **different** values is refused (401) — a proxy that injects one while the client sends the other must not silently pick a winner.

A key looks like `pc_live_` (or `pc_test_` on a [sandbox workspace](https://developers.processcart.com/guides/sandbox)) followed by 43 URL-safe characters. The first twelve characters (`pc_live_8Jq3`) are the key's display prefix, the only part ever shown again.

### When authentication fails

A 401 is an `application/problem+json` body with a stable `code` and a `WWW-Authenticate: Bearer realm="processcart"` header:

```json
{
  "type": "https://developers.processcart.com/errors#key_revoked",
  "title": "API key revoked",
  "status": 401,
  "detail": "This API key was revoked, or its rotation grace period has ended.",
  "code": "key_revoked",
  "requestId": "req_5f1c…"
}
```

| `code` | Meaning |
| --- | --- |
| `unauthorized` | No credential, or two different credentials. |
| `invalid_api_key` | The key is malformed or unknown. |
| `key_revoked` | The key was revoked, or its rotation grace period has ended. |

## Scopes

A key holds one or more action scopes. Each operation names the scope it needs (it is in every reference page and in the OpenAPI document as `x-processcart-scope`); a key without it gets 403 `insufficient_scope` with the scope in `detail`.

| Scope | Label | Allows |
| --- | --- | --- |
| `runs:read` | Read runs | List and read runs, tasks, field values, comments and activity. |
| `runs:write` | Write runs | Start runs, set field values, complete / reopen tasks, comment, upload files, trash and restore. |
| `approvals:decide` | Decide approvals | Approve or reject approval tasks (acts with Admin authority on approvals). |
| `workflows:read` | Read workflows | List workflows, read their published schema and export the portable template. |
| `workflows:write` | Write workflows | Create workflows from a template JSON and change workflow settings (name, description, folder, Key Date field, archive / unarchive). |
| `views:read` | Read saved views | List saved views and query the runs board. |
| `views:write` | Write saved views | Create saved views and update views owned by the key's creator; delete any shared view in the workspace (Admin power, audited). |
| `webhooks:manage` | Manage webhooks | Create, update, test, disable and delete webhooks; read and redeliver deliveries. |
| `schedules:manage` | Manage schedules | Create, update, pause, resume, preview and delete recurrence schedules, and start a schedule's run now (the response is the full Run with its field values). |
| `rules:manage` | Manage automation rules | Create, update, enable, disable and delete automation rules, and read their execution log. A rule's actions change runs (fields, status, priority, labels, task assignees), notify members and call webhooks. |
| `users:read` | Read users & groups | List members and groups (ids, names, roles). |
| `*` | Full access | Every scope, present and future. |

`*` is every scope, present and future — an Admin's "full access" key. `approvals:decide` is never implied by `runs:write`: a key that holds it decides approvals with Admin authority. `schedules:manage` covers [Schedules](https://developers.processcart.com/guides/schedules) and `rules:manage` [Automation rules](https://developers.processcart.com/guides/automation-rules); an automation rule for every workflow also needs a key without a workflow or folder allowlist.

`GET /v1/me` needs no particular scope — any valid key may identify itself — and `GET /v1/health` needs no key at all.

## Read-only keys

A key created as **read-only** may only make `GET` and `HEAD` requests, whatever scopes it holds. A read-only key with `webhooks:manage` can list webhooks and read deliveries but never create, change or redeliver one; any other verb is a 403 `read_only_key`. Hand a read-only key to anything that only needs to look.

## Allowlists
A key may be restricted to some workflows, to the workflows inside some folders, or both. The allowlist is the API's restriction — a key minted by an Admin otherwise sees everything the Admin sees.

- **Run-scoped operations** (anything under `/v1/runs/{id}`, `POST /v1/runs`, the export) resolve the run's or body's workflow and refuse one outside the allowlist with **403 `not_allowed_for_key`**.
- **Lists** (`GET /v1/runs`, `GET /v1/workflows`, `GET /v1/views/{id}/runs`, `GET /v1/schedules`, `GET /v1/automation-rules`) fold the allowlist into the query: rows outside it are simply absent, never a 403. A restricted key's rules list also omits every rule for every workflow, and its execution list (`GET /v1/automation-rules/{id}/executions`) shows only executions on runs of its allowed workflows.
- **Schedules and automation rules:** an item, or a create, for a workflow outside the allowlist is 403 `not_allowed_for_key`, and so is any rule for every workflow — that takes a key without an allowlist ([Schedules](https://developers.processcart.com/guides/schedules#allowlists) · [Automation rules](https://developers.processcart.com/guides/automation-rules#allowlists)).
- **Workflows:** `GET /v1/workflows/{id}` outside the allowlist is a 403; `POST /v1/workflows` is refused for a workflow-restricted key (the new workflow could never be in its list), and a folder-restricted key must create inside one of its folders (`folderId`).
- **Webhooks:** a key restricted by **folder** cannot use the webhook routes at all (403, "mint a workflow-restricted key for webhooks"). A key restricted by **workflow** may create or update a webhook only when the webhook's `workflowIds` is a non-empty subset of the key's workflows, lists and reads only such webhooks, and sees only deliveries about those workflows (plus `webhook.test` deliveries).
- An unfoldered workflow is outside every folder allowlist.

Allowlists are checked on every request against the workspace, so a workflow moved out of an allowed folder stops matching at once.

## Rotation

**Rotate** in Settings → API mints a replacement key and keeps the old secret working for **24 hours** (`graceExpiresAt` on the old key), so a deploy can swap secrets without an outage. After the grace period the old key answers 401 `key_revoked`. An `Idempotency-Key` stored under the old key replays for the replacement too ([Idempotency](https://developers.processcart.com/guides/idempotency)).

## Revocation

**Revoke** stops a key immediately: the next request answers 401 `key_revoked`. Revocation cannot be undone — create a new key instead. Keys outlive their creator, so revoke a departing member's keys when they leave; the request log on the same page shows what each key has been doing.

## Request log and audit

Settings → API keeps a log of every authenticated request (route template, status, latency, request id — never the body or the query) and the audit trail records every key created, rotated or revoked. Quote a response's `X-Request-Id` to support and it can be found there.

## `GET /v1/me`

The first call every integration should make: it returns the workspace and the key behind the credential, with its scopes and the published rate limit. Use it to confirm a key before relying on it.

```bash
curl https://app.processcart.com/api/v1/me -H "Authorization: Bearer pc_live_…"
```

`org.labels` lists the workspace's status labels, in its own order, as `{ "id": "rush", "name": "Rush", "color": "#dc2626" }` — the ids a run's `labels`, the runs filter `labels=`, a view's `query.labels` and an automation rule's label actions take. It is empty when the workspace defines none; labels are managed in the app (Settings → Organization), not through the API.
