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

# Authorization

> Exchange API credentials for a JWT and call Fluide product APIs from your integration.

Fluide Connect uses a **two-step** model: exchange your developer API key and secret for a short-lived JWT, then call product APIs with the JWT and your API key.

## Prerequisites

You already have a verified developer account and API credentials from the Connect dashboard (**API Keys**):

* `apiKey` — e.g. `fl_dev_...`
* `apiSecret` — shown once at provisioning; store it in your secret manager

<Note>
  If you need to view or rotate credentials, sign in to Connect. The API secret is never returned again from `GET /api/v1/authorize/current`.
</Note>

## Exchange key + secret for an access token

Send your credentials only to the token endpoint. Never include the secret on HR, payroll, payments, or other product routes.

<Card title="API reference — POST /authorize/token" icon="key" href="/api-reference/auth/exchange-api-key-for-access-token">
  Interactive playground, request headers, and response schema.
</Card>

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "$FLUIDE_BASE_URL/api/v1/authorize/token" \
    -H "X-Fluide-Api-Key: $FLUIDE_API_KEY" \
    -H "X-Fluide-Api-Secret: $FLUIDE_API_SECRET" \
    -H "X-Fluide-Client-Id: fluide-developer"
  ```

  ```javascript Node.js theme={null}
  const baseUrl = process.env.FLUIDE_BASE_URL;

  const res = await fetch(`${baseUrl}/api/v1/authorize/token`, {
    method: 'POST',
    headers: {
      'X-Fluide-Api-Key': process.env.FLUIDE_API_KEY,
      'X-Fluide-Api-Secret': process.env.FLUIDE_API_SECRET,
      'X-Fluide-Client-Id': 'fluide-developer',
    },
  });

  if (!res.ok) throw new Error(`Token exchange failed: ${await res.text()}`);
  const { accessToken } = await res.json();
  ```
</CodeGroup>

Response (abbreviated):

```json theme={null}
{
  "accessToken": "eyJ...",
  "exp": 1710000000,
  "tenantId": "...",
  "fluideClientId": "fluide-developer",
  "authContextPath": "/api/v1/auth-context/..."
}
```

<Warning>
  Use the API secret only for token exchange. Never send it on product API routes.
</Warning>

## Test in the API playground

Every product endpoint in [API reference](/api-reference) includes an interactive playground. Requests without the right headers are rejected — you will see `Authorization field missing` or `401` if any are omitted.

<Steps>
  <Step title="Exchange a token first">
    Run the token exchange above (curl or Node.js). Copy the `accessToken` value.
  </Step>

  <Step title="Click Authorize on a product endpoint">
    Open any HR, Payroll, Pay, Books, or Utils endpoint and click **Authorize**. Fill in all three fields:

    | Playground field       | Value                                                             |
    | ---------------------- | ----------------------------------------------------------------- |
    | **Bearer**             | Paste the raw JWT (`eyJ...`) — do not include the `Bearer` prefix |
    | **X-Fluide-Api-Key**   | Your `fl_dev_...` key (pre-filled with a placeholder; replace it) |
    | **X-Fluide-Client-Id** | `fluide-developer` (pre-filled)                                   |
  </Step>

  <Step title="Send the request">
    Click **Send**. Health checks such as `GET /api/v1/hr/health` use the same headers as business endpoints — there is no anonymous access.
  </Step>
</Steps>

<Tip>
  `X-Fluide-Client-Id` must match the client ID embedded in your access token (`fluide-developer`). `X-Fluide-Api-Key` must match the API key bound to that token.
</Tip>

## Call Fluide APIs

Send **both** the Bearer token and your API key on every product request.

| Header               | Value                  |
| -------------------- | ---------------------- |
| `Authorization`      | `Bearer <accessToken>` |
| `X-Fluide-Api-Key`   | Your `fl_dev_...` key  |
| `X-Fluide-Client-Id` | `fluide-developer`     |

<CodeGroup>
  ```bash curl theme={null}
  curl -X GET "$FLUIDE_BASE_URL/api/v1/hr/health" \
    -H "Authorization: Bearer $FLUIDE_ACCESS_TOKEN" \
    -H "X-Fluide-Api-Key: $FLUIDE_API_KEY" \
    -H "X-Fluide-Client-Id: fluide-developer"
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(`${baseUrl}/api/v1/hr/health`, {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'X-Fluide-Api-Key': process.env.FLUIDE_API_KEY,
      'X-Fluide-Client-Id': 'fluide-developer',
    },
  });

  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  const health = await res.json();
  ```
</CodeGroup>

<Tip>
  Cache the `accessToken` until `exp` and refresh before it expires. See [First request](/getting-started/first-request) for an end-to-end walkthrough.
</Tip>

For **organization vs partner (multi-merchant) tenancy**, see [Multi-tenancy](/getting-started/multi-tenancy).

## Partner acting-client headers (ISV / multi-merchant)

When you integrate **many end-customers** under your service partner org, product APIs need two **extra headers** on top of Bearer + API key:

| Header                | Value                                                              |
| --------------------- | ------------------------------------------------------------------ |
| `X-Workspace-Id`      | Workspace UUID (from `GET /api/v1/workspaces`)                     |
| `X-Acting-Company-Id` | Client company UUID (from `GET /api/v1/workspaces/{id}/companies`) |

In the **API reference playground**, these appear as optional **header parameters** on HR, Payroll, Pay, Books, and Utils endpoints — fill them in per request when scoping to a merchant. Direct single-org integrations can omit them.

<CodeGroup>
  ```bash curl theme={null}
  curl -X GET "$FLUIDE_BASE_URL/api/v1/hr/employees?limit=10" \
    -H "Authorization: Bearer $FLUIDE_ACCESS_TOKEN" \
    -H "X-Fluide-Api-Key: $FLUIDE_API_KEY" \
    -H "X-Fluide-Client-Id: fluide-developer" \
    -H "X-Workspace-Id: $FLUIDE_WORKSPACE_ID" \
    -H "X-Acting-Company-Id: $FLUIDE_COMPANY_ID"
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(`${baseUrl}/api/v1/hr/employees?limit=10`, {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'X-Fluide-Api-Key': process.env.FLUIDE_API_KEY,
      'X-Fluide-Client-Id': 'fluide-developer',
      'X-Workspace-Id': process.env.FLUIDE_WORKSPACE_ID,
      'X-Acting-Company-Id': process.env.FLUIDE_COMPANY_ID,
    },
  });
  ```
</CodeGroup>

## Rotate credentials

If a secret is exposed, rotate it from the Connect dashboard or with a valid Bearer session:

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "$FLUIDE_BASE_URL/api/v1/authorize/rotate-secret" \
    -H "Authorization: Bearer $FLUIDE_ACCESS_TOKEN" \
    -H "X-Fluide-Client-Id: fluide-developer"
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(`${baseUrl}/api/v1/authorize/rotate-secret`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${sessionToken}`,
      'X-Fluide-Client-Id': 'fluide-developer',
    },
  });

  if (!res.ok) throw new Error(`Rotate failed: ${await res.text()}`);
  const { apiSecret } = await res.json();
  ```
</CodeGroup>

Update your integration and exchange a new token with the new secret.
