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

# API credentials and access tokens

> Exchange your API key for a short-lived, scoped user access token.

## Obtain your API credentials

Once your application is created in the Developer Console, select it to find its **API Key**, **User ID**, and **Account ID**.

<Info>
  **Where to find them:** the [Developers page](https://uni.staging.fluzapp.com/developers), inside the application you created. These credentials power API key authorization — primarily administrative calls and generating user access tokens.
</Info>

![Obtain API credentials](https://files.readme.io/d1b78cd2ecfef1f945a9685f81efe0a8b30b7324d32d5ee62e610bced35585b4-obtain_api_creds.gif)

Every Fluz API request uses a **user access token** in the `Authorization` header. You mint the token by calling the `generateUserAccessToken` mutation with your **API Key** in the `Authorization: Basic <API_KEY>` header, passing the **User ID** and **Account ID** from the console as arguments.

## Generate an access token

```bash theme={null}
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
  -H "Authorization: Basic <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation ($userId: UUID, $accountId: UUID, $scopes: [ScopeType!]!) { generateUserAccessToken(userId: $userId, accountId: $accountId, scopes: $scopes) { token refreshToken scopes } }",
    "variables": {
      "userId": "<YOUR_USER_ID>",
      "accountId": "<YOUR_ACCOUNT_ID>",
      "scopes": ["LIST_OFFERS", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
    }
  }'
```

The response contains the access token, a refresh token, and the scopes the token carries:

```json theme={null}
{
  "data": {
    "generateUserAccessToken": {
      "token": "eyJhbGciOi...",
      "refreshToken": "eyJhbGciOi...",
      "scopes": ["LIST_OFFERS", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
    }
  }
}
```

<Note>
  Select `refreshToken` explicitly. If you request only `token` and `scopes`, you never receive one, and your only option when the access token expires is to mint a new one from scratch. See [Refresh an expired access token](/get-started/refresh-expired-access-token).
</Note>

### Identifying the user

`scopes` is the only always-required argument. Identify the user in one of two ways:

<ParamField body="userId + accountId" type="UUID">
  For applications operating on **your own account**. Both values are shown on your application in the Developer Console.
</ParamField>

<ParamField body="externalReferenceId" type="String">
  For **OAuth applications** operating on a customer's account — your own identifier for that user, the same value passed as `external_id` during the OAuth authorization flow. When you provide it, `userId` and `accountId` are optional. See [Client-facing OAuth grant flow](/client-facing-o-auth-grant-flow).
</ParamField>

<ParamField body="seatId" type="UUID">
  Optional. Selects which seat transacts. Defaults to the most recently created seat.
</ParamField>

## Use the token

Attach the token to every GraphQL request against the transactional graph:

```bash theme={null}
curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"query":"query { getMerchants(name: \"Burger King\") { name slug } }"}'
```

<Warning>
  Never ship your API key to a browser or mobile client. Mint access tokens server-side and forward only the token to the client if you must.
</Warning>

## If the token request returns 401

A rejected API key returns the same message whether it is unknown, malformed, or from the wrong environment:

```json theme={null}
{ "error": "Error verifying API basic token" }
```

The one exception is a real key on an application that has been disabled — that returns a `403` naming the status instead: `Application <app_id> is not in a valid status: <STATUS>.`

Work through these in order.

<AccordionGroup>
  <Accordion title="You are using credentials from the wrong environment">
    This is the most common cause. Staging and live have **separate applications and separate credentials** — a key created in one is unknown to the other, and the error looks identical either way.

    Check where you created the application:

    | Console                              | Environment | Endpoint the key works against            |
    | ------------------------------------ | ----------- | ----------------------------------------- |
    | `uni.staging.fluzapp.com/developers` | Staging     | `transactional-graph.staging.fluzapp.com` |
    | `fluz.app/for-developers`            | Live        | `transactional-graph.fluzapp.com`         |

    A live key sent to the staging endpoint fails here, and the reverse is also true. If you only have a live application, register a second one in staging.
  </Accordion>

  <Accordion title="The API key does not match the application">
    Your API key is a base64-encoded `app_id:app_secret` pair. Copy it whole from the console rather than reassembling it, and do not base64-encode it again — it is already encoded. Send it verbatim:

    ```
    Authorization: Basic <API_KEY>
    ```

    If the key was regenerated in the console, older copies stop working immediately.
  </Accordion>

  <Accordion title="The application is not in an accepted state">
    Applications only authenticate while active. A deleted application is indistinguishable from a bad key — the same `401` comes back. A **disabled** application is the one case that looks different: the response is a `403` with `Application <app_id> is not in a valid status: <STATUS>.` Confirm the application still exists in the console for the environment you are calling and has not been disabled.
  </Accordion>

  <Accordion title="The userId or accountId belongs to a different application">
    `userId` and `accountId` must be the values shown on the same application as the API key. Mixing IDs from one application with the key from another fails.
  </Accordion>
</AccordionGroup>

<Tip>
  Confirm the endpoint host matches the console you created the credentials in. Environment mismatch accounts for most first-run `401`s.
</Tip>

## Mint a new token before expiry

Access tokens are short-lived. Mint a new one before the current one expires — don't wait for a `401`. See [Refresh an expired access token](/get-started/refresh-expired-access-token) for the exact call, and [Authentication](/concepts/authentication) for the full flow, including OAuth grants for customer-scoped tokens.

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart: your first gift card purchase" icon="gift" href="/quickstart/first-gift-card">
    Run the full happy path end to end — deposit funds, buy a gift card, and reveal it in the sandbox.
  </Card>

  <Card title="Replace an expired token" icon="rotate-cw" href="/get-started/refresh-expired-access-token">
    Mint a fresh access token when the current one expires.
  </Card>
</CardGroup>

<StickyContactSalesBanner />
