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

# Authentication

BrandWallet API uses a two-credential exchange mechanism to produce access tokens. You send your **API key** — which identifies your [Connection Type](/integrations/quickstart) — and an **API secret** — which identifies the organization account in your system — to receive a Bearer token scoped to that organization.

Every subsequent API call includes this token in the `Authorization` header.

## Endpoints

Two auth endpoints cover the whole lifecycle:

| Endpoint | Purpose |
| :- | :- |
| `POST /auth/token` | Exchange API key + secret for an access token |
| `POST /auth/refreshtoken` | Rotate the access token / refresh token pair |

## Credentials

| Credential | Purpose | Lifetime |
| :- | :- | :- |
| **API Key** | Identifies your Connection Type. Issued once and used with every organization you connect. | Permanent (until rotated) |
| **API Secret** | Identifies the customer organization. One secret per organization, collected during organization onboarding. | Permanent (until rotated) |

<Warning>
  Your API key and secrets grant access to organization data. Never embed them in client-side code or expose them in a browser. Only transmit them over HTTPS.
</Warning>

## Get a token

Call `POST /auth/token` with your credentials as request headers — no request body is required. Both credentials are validated as a pair: a valid API key with an API secret that belongs to another Connection Type is rejected. The token is issued on behalf of the organization's account and is scoped to that organization.

### Request headers

<ParamField header="x-api-key" type="string" required>
  The API key issued to your Connection Type.
</ParamField>

<ParamField header="x-api-secret" type="string" required>
  The API secret issued for the connected organization.
</ParamField>

### Response fields

<ResponseField name="accessToken" type="string">
  The JWT access token. Pass this value as `Bearer` in the `Authorization` header on all API requests.
</ResponseField>

<ResponseField name="tokenType" type="string">
  The token type. Always `Bearer`.
</ResponseField>

<ResponseField name="expiresIn" type="number">
  Token expiry expressed as a Unix epoch timestamp in milliseconds. Compare this value against the current time to determine when to refresh.
</ResponseField>

<ResponseField name="refreshToken" type="string">
  A token you can use to renew the session without re-sending your credentials. See [Refresh a token](#refresh-a-token) below.
</ResponseField>

## Call the API

Include `Authorization: Bearer <accessToken>` on every subsequent request. BrandWallet validates this token on each call and rejects requests with missing, expired, or malformed tokens. When your token expires, either refresh it or request a new one with your credentials.

## Refresh a token

Instead of re-sending your credentials every time a token expires, call `POST /auth/refreshtoken` with the current `accessToken` and `refreshToken` pair. BrandWallet returns a new token pair and immediately invalidates the old refresh token.

<Warning>
  Refresh tokens are rotated — each successful call invalidates the token you send and returns a brand-new pair. Always replace both stored tokens immediately after a successful refresh. Resending an old refresh token fails with `_invalid_refresh_token`.
</Warning>

### Body parameters

<ParamField body="accessToken" type="string" required>
  The access token issued together with the refresh token. This may be expired — the refresh endpoint accepts it regardless of expiry status.
</ParamField>

<ParamField body="refreshToken" type="string" required>
  The refresh token from the same token response. Must be the latest token in the rotation; previously used tokens are invalidated.
</ParamField>

The response has the same shape as `POST /auth/token`: a new `accessToken`, `tokenType`, `expiresIn`, and `refreshToken`.

## Error codes

| Code | HTTP Status | Meaning |
| :- | :- | :- |
| `_invalid_payload` | 400 | The `x-api-key` or `x-api-secret` header is missing from the `/auth/token` request. |
| `_invalid_credentials` | 401 | The API key and API secret pair is invalid or does not match a known organization. |
| `_invalid_refresh_token` | 400 | The refresh token is unknown, expired, or has already been rotated. |
| `_jwt_expired` | 401 | The access token has passed its expiry time. Refresh or re-request a token. |
| `_jwt_malformed` | 401 | The access token is not a valid JWT and cannot be parsed. |

For the complete list of error codes returned across all endpoints, see the [Error Reference](/api/errors).

<RequestExample>
  ```bash POST /auth/token theme={null}
  curl --request POST \
    --url "$BASE_URL/auth/token" \
    --header 'x-api-key: YOUR_API_KEY' \
    --header 'x-api-secret: ORGANIZATION_SECRET'
  ```

  ```bash POST /auth/refreshtoken theme={null}
  curl --request POST \
    --url "$BASE_URL/auth/refreshtoken" \
    --header 'Content-Type: application/json' \
    --data '{
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
      "refreshToken": "946d2689-c4e4-4ca2-a194-..."
    }'
  ```

  ```bash Authenticated request theme={null}
  curl "$BASE_URL/v1/customers/find?q=5551234567" \
    --header 'Authorization: Bearer ACCESS_TOKEN'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
    "tokenType": "Bearer",
    "expiresIn": 1754126400000,
    "refreshToken": "946d2689-c4e4-4ca2-a194-..."
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "statuscode": 401,
    "errorcode": 401,
    "message": "_invalid_credentials",
    "description": "",
    "timestamp": 1754040000000,
    "path": "/auth/token",
    "method": "POST"
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "statuscode": 400,
    "errorcode": 400,
    "message": "_invalid_refresh_token",
    "description": "",
    "timestamp": 1754040000000,
    "path": "/auth/refreshtoken",
    "method": "POST"
  }
  ```
</ResponseExample>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.