> For the complete documentation index, see [llms.txt](https://docs.drizz.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.drizz.dev/automate-and-integrate/authenticate.md).

# Authenticate

Every Drizz API call requires an access token, obtained by exchanging a client ID and secret through the OAuth 2.0 client credentials flow.

With those two values set, everything on this page runs as written.

|                  |                                                            |
| ---------------- | ---------------------------------------------------------- |
| **Flow**         | OAuth 2.0 client credentials                               |
| **Content-Type** | `application/json`                                         |
| **Returns**      | `access_token`, `token_type`, `expires_in`                 |
| **Valid for**    | 86400 seconds — 24 hours                                   |
| **Watch out**    | Send the token as `x-api-key`, not `Authorization: Bearer` |

## Prerequisites

* Client ID and client secret, issued by Drizz
* Auth host (`<auth-domain>`) for your organization
* Audience string (`<DRIZZ_API_AUDIENCE>`) for your organization
* `curl` and `jq`, or an equivalent HTTP client
* A CI secret store for the client secret

## Copy this

{% tabs %}
{% tab title="Inspect the response" %}

```bash
# Exchange credentials for a 24-hour access token
curl -X POST "https://<auth-domain>/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "<your_client_id>",
    "client_secret": "<your_client_secret>",
    "audience": "<DRIZZ_API_AUDIENCE>",
    "grant_type": "client_credentials"
  }'
```

{% endtab %}

{% tab title="Capture into a variable" %}

```bash
# Capture the token so the rest of the script can use it
export DRIZZ_API_KEY=$(curl -s -X POST "https://<auth-domain>/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "<your_client_id>",
    "client_secret": "<your_client_secret>",
    "audience": "<DRIZZ_API_AUDIENCE>",
    "grant_type": "client_credentials"
  }' | jq -r .access_token)
```

{% endtab %}
{% endtabs %}

## Endpoint

```
POST https://<auth-domain>/oauth/token
```

This is the only call that does not use `<DRIZZ_API_BASE_URL>`. The token is issued by the identity service, not the Drizz API host.

## Request headers

| Header         | Required | Value              |
| -------------- | -------- | ------------------ |
| `Content-Type` | Yes      | `application/json` |

## Request parameters

| Name            | Type   | Required | Max length | Description                                           |
| --------------- | ------ | -------- | ---------- | ----------------------------------------------------- |
| `client_id`     | string | Yes      | 128        | Client identifier issued to you. Example: `abc123xyz` |
| `client_secret` | string | Yes      | 256        | Client secret issued to you                           |
| `audience`      | string | Yes      | 256        | The Drizz API audience — `<DRIZZ_API_AUDIENCE>`       |
| `grant_type`    | string | Yes      | 32         | Always `client_credentials`                           |

Send all four exactly as issued. A wrong `audience` fails the same way wrong credentials do.

## Response

```json
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 86400
}
```

| Field          | Type   | Description                                  |
| -------------- | ------ | -------------------------------------------- |
| `access_token` | string | The bearer token, in JWT format              |
| `token_type`   | string | Token type — `Bearer`                        |
| `expires_in`   | number | Validity in seconds — `86400`, i.e. 24 hours |

## Use the token

Send the token in the `x-api-key` header on every authenticated Drizz API call:

```bash
curl -X POST "$DRIZZ_BASE_URL/testplan/run" \
  -H "x-api-key: $DRIZZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"test_plan_id": "ADuF4ViN", "apks": {"com.shopease.android": "1.0.0"}}'
```

`token_type` is returned as `Bearer`, but Drizz reads the token from `x-api-key`. An `Authorization: Bearer` header is ignored and the call is rejected as unauthenticated.

## Token lifetime

| Rule             | Detail                                                                                |
| ---------------- | ------------------------------------------------------------------------------------- |
| Validity         | 24 hours from issue                                                                   |
| Per pipeline run | Request one fresh token at the start of each run                                      |
| Storage          | Run-scoped only. Keep the client secret in a CI secret store, never in the repository |
| Expiry symptom   | A pipeline that worked yesterday fails today as unauthenticated                       |

## Errors

| Code    | Meaning           | What to do                                                                |
| ------- | ----------------- | ------------------------------------------------------------------------- |
| **200** | Success           | Read `access_token` from the response                                     |
| **400** | Bad Request       | Validate the payload — all four parameters, JSON body                     |
| **429** | Too Many Requests | Over the rate limit. Request one token per pipeline run, not one per call |
| **500** | Server Error      | Contact `support@drizz.dev`                                               |
| **502** | Bad Gateway       | Retry, or check service availability                                      |

The status code returned for an invalid or expired token is not confirmed. Handle it by the response body as well as the code — see [Errors & limits](/automate-and-integrate/errors-and-limits.md).

## Common mistakes

| What you did                            | What happens                                          |
| --------------------------------------- | ----------------------------------------------------- |
| Sent `Authorization: Bearer <token>`    | Rejected as unauthenticated. Use `x-api-key`          |
| Reused yesterday's token                | Expired after 24 hours. Request a fresh one per run   |
| Sent form-encoded credentials           | The endpoint expects `Content-Type: application/json` |
| Guessed at the `audience` value         | Fails like a bad credential. Ask your Drizz contact   |
| Committed the client secret to the repo | Anyone with repo access can trigger your runs         |

## Next

* [Upload a build](/automate-and-integrate/upload-a-build.md) — register a binary
* [Trigger a run](/automate-and-integrate/trigger-a-run.md) — start a test plan
* [API overview](/automate-and-integrate/api-overview.md) — the whole flow in one page

***

*Last updated: 6 August 2026*
