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

# Authentication

> Authenticate with a bearer token and scope requests to an environment.

## Bearer token

Send your token in the `Authorization` header on every request:

```bash theme={null}
curl -H "Authorization: Bearer $ELEMENTARY_TOKEN" \
  https://prod.api.elementary-data.com/public/beta/environments
```

Both **personal tokens** and **account tokens** are accepted. The token resolves
to an account, and requests are limited to the environments that token may view.

## Creating a token

Generate a token in the Elementary app, then send it as the bearer token shown
above. The token is displayed **only once** — copy it and store it securely.

<CardGroup cols={2}>
  <Card title="Personal token" icon="user" href="https://app.elementary-data.com/settings/user-tokens">
    **User → Personal Tokens.** User-scoped: inherits your own workspace
    permissions, so it can read exactly the environments and assets you can.
    Best for personal scripts and exploration.
  </Card>

  <Card title="Account token" icon="building" href="https://app.elementary-data.com/settings/account-tokens">
    **Account → Account Tokens.** Account-scoped with "Can View" permissions and
    not tied to a single user. Best for shared services, CI, and integrations.
    Creating one requires the *Manage account tokens* permission.
  </Card>
</CardGroup>

In either case, click **Generate token** and copy the value into the
`Authorization: Bearer` header.

### Which token should I use?

Both work identically on the wire — the difference is what the token is tied to:

* Use a **personal token** for interactive work: notebooks, one-off scripts, and
  local exploration. Its access mirrors yours and is revoked automatically when
  your own access changes, so it's convenient but tied to your account lifecycle
  (a token stops working if you leave the workspace or lose a permission).
* Use an **account token** for anything long-lived or shared: CI jobs, backend
  services, and third-party integrations. It isn't attached to a person, so it
  keeps working through team changes and offboarding — which also makes it the
  token you must rotate and guard most carefully.

<Warning>
  Treat tokens like passwords: never share or commit them, rotate them
  regularly, and revoke a token immediately if it may have been exposed.
</Warning>

## Environment scope

Every resource except `GET /environments` is nested under an environment:

```
/public/beta/{env_id}/assets
```

Call `GET /environments` first to discover the ids you can access. Passing an
`env_id` you don't have access to returns `403`.

## Visibility & permissions

Access is governed by your token's role at three levels:

1. **Environment access** — the token must be able to view the environment (as
   returned by `GET /environments`). Otherwise every request under that
   `env_id` returns `403`.
2. **Resource scope** — the asset, column, and lineage endpoints require the
   *assets* view scope. A role that can reach the environment but isn't granted
   the assets resource returns `403`. Test endpoints (**coming soon**) require
   the *tests* view scope.
3. **Per-asset access** — see below.

If your token's role is restricted to a subset of an environment's assets
(resource-group permissions), the API only ever returns the assets — and the
columns and lineage edges derived from them — that you're allowed to see:

* **List endpoints** silently omit assets you can't view. Asset-lineage edges
  are returned only when **both** of their endpoints are visible to you; an
  edge that touches a restricted asset is dropped entirely.
* Column-lineage edges are returned only when the downstream column's asset is
  visible to you. A returned edge's `upstream_column_id` may reference a
  restricted upstream asset; only the id is exposed, not its metadata.
* **Get-one endpoints** (`/assets/{id}`, `/columns/{id}`, and coming-soon
  `/tests/{id}`) return `404` for an object you can't view — the same response
  as a nonexistent object, so the API never discloses that a restricted asset
  or test exists.

Because filtering is applied per page after rows are read, a page may contain
fewer than `limit` items while `has_more` is still `true`. Always iterate on
`has_more`, not on item count — see [Pagination](/api/pagination).

## Errors

| Status | Code                | Meaning                                      |
| ------ | ------------------- | -------------------------------------------- |
| `401`  | `unauthenticated`   | Missing or invalid token                     |
| `403`  | `permission_denied` | Token can't access the requested environment |
| `404`  | `not_found`         | Environment or object doesn't exist          |

See [Errors](/api/errors) for the full error contract.
