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

# Authentication

> Bearer tokens, scopes, and partner isolation.

Every request to the Admin API is authenticated with a bearer token:

```text theme={null}
Authorization: Bearer {access_token}
```

Two kinds of token are accepted on the same endpoints:

* **User session tokens** — minted when someone signs in to the dashboard.
  They carry the user's role and permissions.
* **M2M tokens** — minted by your API client via the OAuth
  `client_credentials` grant. They carry OAuth **scopes**.

Both carry your organization ID, which is the partner key: every query runs
scoped to that organization.

## Scopes

M2M tokens are gated per resource. Request only what you need:

| Scope                                      | Grants                                               |
| ------------------------------------------ | ---------------------------------------------------- |
| `consignments:read` / `consignments:write` | List, retrieve / create, update, delete consignments |
| `customers:read` / `customers:write`       | Customer records                                     |
| `catalogue:read` / `catalogue:write`       | Product catalogue                                    |
| `carriers:read` / `carriers:write`         | Carrier accounts and shipping setup                  |
| `classify:read`                            | HS classification endpoints                          |
| `calculate:read`                           | Duty calculation endpoints                           |

A token missing the required scope gets `403 Forbidden` with an error body
naming the missing scope.

## Token lifetime

Access tokens are short-lived JWTs. Cache the token and re-use it until it
expires (the `expires_in` field of the token response), then request a new
one. Don't mint a fresh token per request.

## Partner isolation

Three independent layers enforce that a token only ever reads its own
partner's data:

1. **Subdomain ↔ organization match** — the Admin API rejects a request when
   the token's organization doesn't match the partner subdomain it arrived on.
2. **Scoped queries** — every database query is scoped to the token's
   organization ID.
3. **Row-level security** — the database itself refuses rows for any other
   organization, even if a query were mis-scoped.
