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

# Authentication

> Exchange your client credentials for an access token, and send it on every request.

The API uses the OAuth2 `client_credentials` grant. You exchange a client ID
and a client secret for a short-lived access token, then send that token on
every other request.

Requests go to the Document Verification deployment in your cloud environment.

## Get credentials

Ask your Trelent admin for a client ID and secret, the list of scopes that your
client holds, and the **base URL** of the environment you are given. Put the
secret in your secret manager.

Every example on these pages reads the base URL from `TRELENT_API_URL`:

```bash theme={null}
export TRELENT_API_URL="..."      # the base URL Trelent gave you
export TRELENT_CLIENT_ID="..."
export TRELENT_CLIENT_SECRET="..."
```

## Get a token

Post a form to `/token`. The body is form-encoded, not JSON.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "$TRELENT_API_URL/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials" \
      -d "client_id=$TRELENT_CLIENT_ID" \
      -d "client_secret=$TRELENT_CLIENT_SECRET" \
      -d "scope=DocumentVerification:*"
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os
    import requests

    BASE = os.environ["TRELENT_API_URL"]

    response = requests.post(
        f"{BASE}/token",
        data={
            "grant_type": "client_credentials",
            "client_id": os.environ["TRELENT_CLIENT_ID"],
            "client_secret": os.environ["TRELENT_CLIENT_SECRET"],
            "scope": "DocumentVerification:*",
        },
        timeout=10,
    )
    response.raise_for_status()
    token = response.json()["access_token"]
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const BASE = process.env.TRELENT_API_URL!;

    const body = new URLSearchParams({
      grant_type: "client_credentials",
      client_id: process.env.TRELENT_CLIENT_ID!,
      client_secret: process.env.TRELENT_CLIENT_SECRET!,
      scope: "DocumentVerification:*",
    });

    const response = await fetch(`${BASE}/token`, {
      method: "POST",
      headers: { "content-type": "application/x-www-form-urlencoded" },
      body,
    });
    if (!response.ok) throw new Error(`token request failed: ${response.status}`);
    const { access_token: token } = await response.json();
    ```
  </Tab>
</Tabs>

The answer holds the token and its lifetime:

```json theme={null}
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "DocumentVerification:*"
}
```

## Send the token

Put the token in the `Authorization` header of every `/v1` request:

```
Authorization: Bearer <access_token>
```

Cache the token until it expires. Read `expires_in` rather than assuming a
value, and do not cache a token whose `expires_in` is `0`.

## Scopes

In most cases, requesting `DocumentVerification:*` gives you access to every
operation, and it is the best path forward if your client holds it.

```
scope=DocumentVerification:*
```

<Note>
  The authorization server grants only the scopes you name in the request, and
  it matches them against your client's registration. If your client does not
  hold `DocumentVerification:*`, ask for the scopes it does hold. Read the
  `scope` field of the answer to confirm what you received.
</Note>

### The individual scopes

Use these if your client holds a narrower set. They are also worth knowing so
that you can read a `403` message, which names the scope it wanted.

| Scope                                           | What it permits                                                                                          |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `DocumentVerification:detections:create`        | Start a detection                                                                                        |
| `DocumentVerification:detections:get`           | Read a detection, and list detections                                                                    |
| `DocumentVerification:detections:delete`        | Delete a detection and its files                                                                         |
| `DocumentVerification:detections:create_sample` | Start a sample run within your [contract allowance](/document-verification/billing) using `X-Sample-Run` |

Wildcards nest. `DocumentVerification:*` covers everything above;
`DocumentVerification:detections:*` covers every detection scope.

## When authentication fails

| Answer                                 | Cause                                                     | What to do                                                                                    |
| -------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `400 unsupported grant_type`           | `grant_type` is not `client_credentials`                  | Correct the token request                                                                     |
| `401 Missing bearer token`             | No `Authorization` header, or it is not a `Bearer` header | Add the header                                                                                |
| `401 Token has expired`                | The token is past `expires_in`                            | Get a new token                                                                               |
| `403 Missing required scopes: <names>` | The token lacks the scopes that the message names         | Ask for the scopes the message names, or for `DocumentVerification:*` if your client holds it |

A `403` names the scopes it wanted. Read them from the message; that is faster
than guessing.
