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

# Authentication

> Flow Payroll uses token-based authentication. Exchange your client credentials for a short-lived access token, then send it on every request.

Every request must include a valid bearer token in the `X-Auth-Token` header. Tokens are issued via the OAuth 2.0 **client credentials** grant.

## How it works

<Steps>
  <Step title="Get credentials">
    You receive a `client_id` and `client_secret` from Flow Payroll (see [Obtaining credentials](#obtaining-credentials)).
  </Step>

  <Step title="Exchange for a token">
    Your application exchanges those credentials for a short-lived access token at the token endpoint.
  </Step>

  <Step title="Call the API">
    Send the token in the `X-Auth-Token` header on every API request.
  </Step>

  <Step title="Refresh before expiry">
    When the token nears expiry, mint a fresh one. Do **not** call the token endpoint on every request.
  </Step>
</Steps>

## Obtaining credentials

Client credentials are issued per integration. Contact your Flow Payroll account manager or [hello@flowpayroll.ai](mailto:hello@flowpayroll.ai) to request a `client_id` / `client_secret` pair.

<Warning>
  Treat `client_secret` like a password. Never embed it in client-side code, mobile apps, or public repositories. Rotate immediately if you suspect exposure.
</Warning>

## Requesting a token

POST your credentials to the token endpoint using the standard OAuth 2.0 client credentials grant.

| Environment | Token endpoint                                     |
| ----------- | -------------------------------------------------- |
| Sandbox     | `https://auth.sandbox.flowpayroll.ai/oauth2/token` |
| Live        | `https://auth.flowpayroll.ai/oauth2/token`         |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://auth.sandbox.flowpayroll.ai/oauth2/token \
    -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"
  ```

  ```http HTTP theme={null}
  POST /oauth2/token HTTP/1.1
  Host: auth.sandbox.flowpayroll.ai
  Content-Type: application/x-www-form-urlencoded

  grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
  ```
</CodeGroup>

Response:

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

Cache the `access_token` in memory and reuse it until shortly before `expires_in` elapses.

## Calling the API

Pass the token as `X-Auth-Token` on every request. If your credentials can access more than one organisation, also send `X-Org-Id` to choose which one the request applies to (see [Choosing an organisation](#choosing-an-organisation)). Optionally include `User-Id` to attribute the call to a specific human user — this is recorded in audit logs but is not required for the request to succeed.

```http theme={null}
GET /payrollconfig HTTP/1.1
Host: api.sandbox.flowpayroll.ai
X-Auth-Token: eyJraWQiOiJ...
X-Org-Id: 01ARZ3NDEKTSV4RRFFQ69G5FAV
User-Id: alice@example.com
```

## Choosing an organisation

One set of credentials can be granted access to many organisations — a bureau integration might serve hundreds. So the token proves **who you are**, not **which organisation** a request is for. You state the organisation per request with the `X-Org-Id` header (the id of the organisation you want to act on).

* **Access to one organisation:** you can omit `X-Org-Id` — it's resolved automatically.
* **Access to more than one:** send `X-Org-Id` with the target organisation's id. If you omit it, the request falls back to your configured default organisation; with no default set, it's rejected so you choose explicitly.
* **Switching organisation** is simply sending a different `X-Org-Id` — no new token, no re-authentication.

To discover which organisations your credentials can act on, call `GET /v1/authentication/my-organisations`.

## Errors

| Status                  | Cause                                                                                                                                                                  | What to do                                                                       |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `401 Unauthorized`      | Missing, malformed, or expired `X-Auth-Token`                                                                                                                          | Mint a fresh token and retry.                                                    |
| `403 Forbidden`         | Token is valid but you don't have access to the organisation in `X-Org-Id` — or you can access more than one organisation and sent no `X-Org-Id` (and have no default) | Send an `X-Org-Id` your credentials are granted; check your organisation access. |
| `429 Too Many Requests` | Rate limit hit                                                                                                                                                         | Back off and retry; do not loop calling the token endpoint.                      |

## Best practices

* **Cache tokens.** Hitting the token endpoint per API call will rate-limit you and adds latency.
* **Refresh proactively.** Renew when \~10% of the token's lifetime remains, not after a `401`.
* **Log the token's `jti` claim**, not the token itself, when correlating requests in your own observability.
* **Set `User-Id`** when a request is initiated by an end user in your product, so the action is traceable in Flow Payroll's audit log.
