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

# Authentication

> How to authenticate a request, what a key looks like, and what each failure means.

Every request to `/api/v1` is authenticated by an API key, not by a signed-in session. There is no cookie or bearer session token that works here.

<div className="pl-availability">
  <div className="pl-availability__row">
    <div className="pl-availability__label">Where</div>
    <div className="pl-availability__value"><span className="pl-path">Settings<span className="pl-path__sep">→</span>API & Integrations</span></div>
  </div>

  <div className="pl-availability__row">
    <div className="pl-availability__label">Your role needs</div>
    <div className="pl-availability__value">Update access to Lead intake API <code>update-companies</code>. Admin have it by default.</div>
  </div>

  <div className="pl-availability__note">Authenticated by a workspace API key, not by a signed-in session. Creating a key needs the same permission as the rest of the API settings tab.</div>
</div>

## Sending the key

Send the key as a bearer token:

```
Authorization: Bearer plk_live_a1B2c3D4_kQ7mZ2xA9f3...
```

If your HTTP client reserves the `Authorization` header, send the same value in `X-API-Key` instead. The middleware checks `Authorization: Bearer` first and falls back to `X-API-Key` only when that header is absent.

## Key anatomy

A key looks like `plk_live_a1B2c3D4_kQ7mZ2xA9f3...`: a prefix, then an underscore, then a secret. The prefix is everything before the *last* underscore in the token; the prefix itself contains underscores, so splitting on the first one would cut it short.

| Piece                   | What it is                                                                                                                                                                                |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `plk_live` / `plk_test` | Which environment issued the key. `live` in production, `test` everywhere else, so a key from the wrong environment fails at the first request instead of writing to the wrong workspace. |
| 8 random characters     | Makes the prefix unique per key. Stored and shown in full. It's the lookup key, not a secret.                                                                                             |
| The secret              | 40 random characters. Only a one-way hash of it is ever stored; it is shown to you exactly once, at creation.                                                                             |

The product only ever shows you a masked form of a key it already issued: the prefix, an ellipsis, then the last four characters of the secret. For example, `plk_live_a1B2c3D4…9f3k`. Those four characters are the tail of the real secret, not part of the prefix.

## Scopes

A key carries one or more scopes, and every route on `/api/v1` needs a specific one except `GET /me`, which only needs a valid key.

| Scope         | Grants                                                       |
| ------------- | ------------------------------------------------------------ |
| `leads:write` | Create and update leads: `POST /leads`, `POST /leads/batch`. |
| `leads:read`  | Read a lead back: `GET /leads/{id}`.                         |
| `tags:read`   | List tags: `GET /tags`.                                      |

A key needs at least one scope; there is no way to create or save one with none ticked. Scopes are enforced per route, not per group, so adding a new read endpoint later can never accidentally inherit write permission from an existing key.

## What each 401 means

`AuthenticateApiKey` returns 401 for every problem with the credential itself: a missing key, a malformed token, an unknown prefix, a revoked key and an expired key. All of these produce the same generic body:

```json theme={"system"}
{
  "message": "Invalid, revoked or expired API key.",
  "error": "invalid_api_key",
  "documentation_url": "https://help.pipelime.ai/en/developers/authentication"
}
```

That single message stands in for all five causes deliberately: telling a caller which of those it hit would let a script probe for keys that exist. The trigger is any malformed or unknown key: no underscore in the token, a leading or trailing underscore, a prefix that doesn't start `plk_`, a prefix over 40 characters, or a prefix that doesn't match a stored key.

One case gets its own message. If the teammate who created the key has since been deleted from the workspace, the key is orphaned (there's no one left for it to act as) and the body reads:

```json theme={"system"}
{
  "message": "This API key is orphaned because the user who created it no longer exists. Create a new key.",
  "error": "invalid_api_key",
  "documentation_url": "https://help.pipelime.ai/en/developers/authentication"
}
```

Both bodies carry the `documentation_url` above, pointing back at this page. It's what a 401 in a script or a log line links to.

Removing someone from the workspace does not orphan their keys on its own; their account still exists elsewhere, so their keys keep working until someone deliberately revokes them.

## The 403: missing scope

A key that authenticates but lacks the scope a route needs gets a 403, not a 401. The credential is valid, it's just missing a capability:

```json theme={"system"}
{
  "message": "This API key is missing the required scope [leads:write].",
  "error": "insufficient_scope",
  "required_scope": "leads:write",
  "granted_scopes": ["tags:read"]
}
```

There is no control in the product to add a scope to an existing key, and rolling a key carries its scopes across unchanged rather than letting you edit them. The fix is a new key created with the scope you need.

## Related

<CardGroup cols={2}>
  <Card title="Create, roll and revoke API keys" icon="key" href="/en/developers/api-keys">
    Where keys are created, how to see one's scopes, and how to roll or revoke it.
  </Card>

  <Card title="API overview" icon="compass" href="/en/developers/overview">
    What the public API is for and what it can do.
  </Card>

  <Card title="Errors reference" icon="circle-alert" href="/en/developers/errors">
    Every status the API returns and what to change before retrying.
  </Card>
</CardGroup>
