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

# Generate an API Token

Mint a short-lived access token using OAuth 2.0 Client Credentials, and use it to authenticate calls to the Oleria API. The Oleria API uses the OAuth 2.0 Client Credentials grant: you exchange a `client_id` and `client_secret` for a short-lived JWT access token, then pass that token as a Bearer credential on every API call.

## Prerequisites

* Network access from your client to the Oleria authorization server and to the Oleria API base URL for your environment.
* A secret manager or environment-variable store to hold the `client_secret` outside of source control.

<Warning>
  Treat your `client_secret` like a password. Never commit it to source control, paste it into chat or tickets, or embed it in client-side code. If you suspect a secret has leaked, rotate it immediately by deleting the key from **Settings → Manage APIs** and creating a new one.
</Warning>

## Get your client credentials

Generate OAuth client credentials directly from the Oleria web app:

1. Sign in to your Oleria workspace and open **Settings → Manage APIs**.
2. Click **Create OAuth application** in the top right.
3. Enter a descriptive name for your integration (for example, `BI nightly export` or `internal access-graph viewer`) and click **Create**.
4. The new application opens in a side panel with the values you need to call the API:
   * **API URL** - the base URL for your API calls.
   * **Authentication URL** - the OAuth token endpoint for your tenant.
   * **Audience** - the OAuth audience to use when requesting a token.
   * **Client ID** - your application's identifier.
   * **Client secret** - shown masked; use the eye icon to reveal it or the copy icon to copy it to your clipboard.
5. Copy the `client_id`, `client_secret`, and **Authentication URL** into your secret manager. Treat the secret like a password.

<Note>
  Use a separate OAuth application for each integration. This makes auditing easier and limits the blast radius if any single credential is compromised.
</Note>

## Token endpoint

| Field        | Value                                                       |
| :----------- | :---------------------------------------------------------- |
| URL          | Your **Authentication URL** from **Settings → Manage APIs** |
| Method       | `POST`                                                      |
| Content-Type | `application/x-www-form-urlencoded`                         |
| Grant type   | `client_credentials`                                        |

The token endpoint URL is tenant-specific and is shown in the **Authentication URL** field when you create an API key.

## Request a token

Send a form-encoded `POST` to the token endpoint. The body must include `grant_type`, `client_id`, and `client_secret`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST YOUR_AUTHENTICATION_URL \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials" \
    -d "client_id=YOUR_CLIENT_ID" \
    -d "client_secret=YOUR_CLIENT_SECRET"
  ```

  ```python Python theme={null}
  import os
  import requests

  resp = requests.post(
      "YOUR_AUTHENTICATION_URL",
      data={
          "grant_type": "client_credentials",
          "client_id": os.environ["OLERIA_CLIENT_ID"],
          "client_secret": os.environ["OLERIA_CLIENT_SECRET"],
      },
  )
  resp.raise_for_status()
  token_payload = resp.json()
  access_token = token_payload["access_token"]
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("YOUR_AUTHENTICATION_URL", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: process.env.OLERIA_CLIENT_ID,
      client_secret: process.env.OLERIA_CLIENT_SECRET,
    }),
  });
  if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
  const { access_token, expires_in } = await res.json();
  ```

  ```go Go theme={null}
  package main

  import (
      "fmt"
      "io"
      "net/http"
      "net/url"
      "os"
      "strings"
  )

  func main() {
      form := url.Values{}
      form.Set("grant_type", "client_credentials")
      form.Set("client_id", os.Getenv("OLERIA_CLIENT_ID"))
      form.Set("client_secret", os.Getenv("OLERIA_CLIENT_SECRET"))

      req, _ := http.NewRequest("POST",
          "YOUR_AUTHENTICATION_URL",
          strings.NewReader(form.Encode()))
      req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      body, _ := io.ReadAll(resp.Body)
      fmt.Println(string(body))
  }
  ```
</CodeGroup>

A successful call returns `200 OK` with a JSON body:

```json theme={null}
{
  "access_token": "eyJraWQiOi...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

| Field          | Description                                                         |
| :------------- | :------------------------------------------------------------------ |
| `access_token` | The JWT to send on subsequent API calls.                            |
| `token_type`   | Always `Bearer`.                                                    |
| `expires_in`   | Lifetime of the token in seconds (typically `3600`, i.e. one hour). |

## Use the token

Send the token as a Bearer credential in the `Authorization` header on every Oleria API request.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://devx.YOUR_TENANT.oleria.io/v1/downloads \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{ "context": "accessInventoryIdentitiesV2" }'
  ```

  ```python Python theme={null}
  resp = requests.post(
      "https://devx.YOUR_TENANT.oleria.io/v1/downloads",
      headers={
          "Authorization": f"Bearer {access_token}",
          "Content-Type": "application/json",
      },
      json={"context": "accessInventoryIdentitiesV2"},
  )
  resp.raise_for_status()
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://devx.YOUR_TENANT.oleria.io/v1/downloads", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${access_token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ context: "accessInventoryIdentitiesV2" }),
  });
  ```
</CodeGroup>

## Token lifetime and reuse

* A token is valid for `expires_in` seconds (typically one hour).
* Reuse the same token for every API call until it is close to expiry. Minting a new token for every request is wasteful and can hit auth-side rate limits.
* Cache tokens in memory inside your client and refresh them when they have fewer than 60 seconds of life remaining.
* Treat access tokens like passwords - keep them in memory only, and avoid writing them to disk or logs.

<Tip>
  If you use a standard OAuth 2.0 client library (for example, `requests-oauthlib` in Python or `golang.org/x/oauth2/clientcredentials` in Go), token caching and automatic refresh are handled for you.
</Tip>

## Rotate or revoke credentials

To rotate a `client_secret`, delete the API key from **Settings → Manage APIs** and create a new one. Roll out the new `client_id` and `client_secret` to every client that uses them before deleting the old key. Plan rotations during a low-traffic window to avoid dropped requests.

## Troubleshooting

| Symptom                                                               | Likely cause                                                                                        | Fix                                                                                                    |
| :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------- |
| `400 invalid_request` from the token endpoint                         | A required field (`grant_type`, `client_id`, or `client_secret`) is missing or misspelled.          | Compare your request body to the [Request a token](#request-a-token) sample.                           |
| `401 invalid_client` from the token endpoint                          | The `client_id` or `client_secret` is wrong, or the credentials are not active in this environment. | Confirm the credentials with Customer Success and verify you are calling the right environment.        |
| `401 Unauthorized` from an API endpoint after a successful token call | The token has expired, or the `Authorization` header is malformed.                                  | Mint a fresh token and ensure the header is exactly `Authorization: Bearer <token>`.                   |
| `403 Forbidden` from an API endpoint                                  | The token is valid but the credential is not scoped for that resource.                              | Contact Customer Success to confirm the credential has the right permissions.                          |
| Token request hangs or times out                                      | Network egress to your Authentication URL host is blocked.                                          | Allow-list the hostname from your **Authentication URL** and your **API URL** in your egress firewall. |

## Contact us

For questions about API authentication, contact us at [support@oleria.com](mailto:support@oleria.com).
