> For the complete documentation index, see [llms.txt](https://docs.peakcommerce.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.peakcommerce.com/developers/api-reference/authentication.md).

# Authentication

Every request to `/api/v1` (other than the public discovery endpoints `/api/v1/openapi.json` and `/.well-known/agent-tools.json`) requires an API key.

## Obtain a key

Admins create keys in the admin app under **Settings → API Keys**. Each key:

* Is tenant-scoped — one key cannot operate across multiple tenants.
* Carries one or more **scopes** — either a coarse scope (`read`, `commerce`, `admin`) or a set of narrow per-resource scopes (see below).
* Has a **rate-limit tier**: `standard`, `premium`, `unlimited`.

## Key format

A key states, in its own prefix, what it is and which data it reaches:

```
sk_live_<64 hex chars>
│  │
│  └── live | test   which data the key reaches
└────── sk   | pk    what the key is allowed to be
```

| Prefix                    | Meaning                                                                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `sk_live_…`               | **Secret.** Server-side only. Carries scopes. This is the key you use for API calls.                                                                               |
| `sk_test_…`               | Secret, against test data. *Not yet available — see* [*Base URL & environments*](/developers/api-reference/base-url.md)*.*                                         |
| `pk_live_…` / `pk_test_…` | **Publishable.** Safe to include in a web page. Restricted to origins you nominate, carries **no** scopes, and is **refused by every endpoint in this reference.** |

The plain-text value is shown only once at creation; only its SHA-256 hash is stored on disk.

> **Keys issued before this format existed** look like `pk_<64 hex chars>` — no `live`/`test` segment. Despite the `pk_` prefix, those keys are **secret**, they still work, and they should be treated with the same care as an `sk_` key. If you hold one, rotate it when convenient so its prefix matches what it actually is.

## Sending a key

Use either header on every request:

```
Authorization: Bearer sk_live_<your_key>
```

…or:

```
X-API-Key: sk_live_<your_key>
```

Both are equivalent. If both are present, `Authorization` wins.

## Coarse scopes

The three original scopes nest:

```
admin  ⊃  commerce  ⊃  read
```

So an `admin` key satisfies a `commerce` requirement, which satisfies a `read` requirement. Each endpoint declares the minimum scope it needs; this is shown on every reference page. These keys are "whole tenant" keys: `admin` is the ceiling and implies everything — including every narrow scope below — `commerce` is the buyer-agent ceiling, and `read` reads everything the coarse surface exposes.

For example: reading the catalog or fetching a cart needs `read`; creating or modifying a cart, adding or removing items, and applying or clearing a promo code (the commerce surface) need `commerce`; managing API keys or webhooks needs `admin`. A `read`-only key receives `403 Forbidden` on any commerce write.

## Narrow per-resource scopes

Alongside the coarse scopes, a key can carry **narrow scopes** that grant authority over one resource rather than the whole tenant — the right shape for an integration or agent that should manage journeys, pages, or components and nothing else. The narrow scopes with live endpoints today:

| Resource   | Scopes                                                                      |
| ---------- | --------------------------------------------------------------------------- |
| Journeys   | `journeys:read` · `journeys:write` · `journeys:publish` · `journeys:delete` |
| Pages      | `pages:read` · `pages:write` · `pages:publish` · `pages:delete`             |
| Components | `components:read` · `components:write` · `components:delete`                |

The expansion rules are deliberate, and worth internalizing before you mint a key:

* **A non-read verb implies its own resource's read, and nothing else.** You cannot edit what you cannot fetch, so `journeys:write` includes `journeys:read`. It includes nothing beyond that.
* **`publish` is never implied by `write`.** Publishing pushes a journey or page live, so a key that may author drafts cannot ship them. The same authority gates the reverse direction: deactivating or archiving a live journey, unpublishing a page, or deleting something that is currently live all require the resource's `publish` scope — anything that changes what customers see is publish-level. (`delete` alone can remove drafts and archived items.)
* **Narrow scopes never expand into coarse ones.** A `journeys:read` key does not satisfy an endpoint gated on coarse `read` — if it did, narrowing would buy nothing, because the key would reach every read route in v1.
* **Coarse `read` and `commerce` reach `catalog:read` only — not every narrow read.** Existing coarse keys keep exactly the access they have always had (catalog reads); they do not silently gain access to journeys, pages, or components as those surfaces narrow.

A few additional names exist in the scope vocabulary but are **reserved** — accepted when creating a key, with no endpoint requiring them yet: `productsets:read`/`write`/`delete`, `rules:read`/`write`/`delete`, and `catalog:write-native`. They are listed here rather than omitted so a key carrying one is not a mystery; grants against them become meaningful as the corresponding endpoints ship.

## Minting narrow-scoped keys

Narrow-scoped keys are created through the v1 admin API — a `POST /api/v1/api-keys` call made with an `admin` key, passing the exact `scopes` array you want (see [Admin - API Keys](/developers/admin-api/admin-api-keys.md)).

The **Settings → API Keys** screen mints coarse, single-scope keys (`read`, `commerce`, or `admin`). Narrow-scoped keys **appear** there — every granted scope is shown on the key — but their scope is edited via the API rather than the screen, whose scope picker is single-choice and would collapse a multi-scope grant down to one value.

## Errors

| Status             | Code                          | Reason                                                                                                                   |
| ------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `401 Unauthorized` | `api_key_required`            | No key presented                                                                                                         |
| `401 Unauthorized` | `invalid_api_key`             | Unknown or malformed key                                                                                                 |
| `401 Unauthorized` | `api_key_revoked`             | Key has been revoked                                                                                                     |
| `401 Unauthorized` | `publishable_key_not_allowed` | A publishable (`pk_…`) key was sent to an endpoint that requires a secret key. Use your `sk_…` key from the server side. |
| `403 Forbidden`    | `insufficient_scope`          | Key is valid but does not carry the required scope                                                                       |

```json
{
  "error": "Insufficient scope. Required: 'commerce'. Your key has: [read]."
}
```

## Revoking a key

Revoke a key from the admin app. Revocation takes effect immediately and is recorded in the audit log.

## Best practices

* **Never** ship a secret (`sk_…`) key in client-side code — including in a bundled front-end app, a mobile binary, or a public repository. Only a publishable (`pk_…`) key is safe to expose, and it deliberately cannot call anything in this reference.
* Use the **least-privilege** scope. Read-only integrations should use `read` keys; an integration that manages one resource should hold that resource's narrow scopes rather than a coarse one — and should only hold `publish` if it genuinely needs to change what is live.
* Rotate keys periodically. Issue a new key, switch your integration, then revoke the old one.
* If a key may be exposed (e.g. logged accidentally), revoke it immediately and re-issue.

## Related

* [Base URL & environments](/developers/api-reference/base-url.md)
* [Admin - API Keys](/developers/admin-api/admin-api-keys.md)
* [Rate limits](/developers/api-reference/rate-limits.md)
* [Errors](/developers/api-reference/errors.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.peakcommerce.com/developers/api-reference/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
