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

# Authentication

> Authenticate requests with an API key.

The Kua API authenticates with an **API key**. Issue one from **Settings → API
keys** in the [dashboard](https://dashboard.kuahr.com/settings/api-keys)
(issuing a key requires two-factor confirmation).

## Using your key

Send the key in the `X-Api-Key` header on every request. The SDKs do this for you
once you construct the client:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.kuahr.com/v1/payroll/runs \
    -H "X-Api-Key: kua_live_your_key_here"
  ```

  ```ts Node theme={null}
  import { Kua } from "@kua/node";

  const kua = new Kua({ apiKey: process.env.KUA_API_KEY! });
  ```

  ```python Python theme={null}
  import os
  from kua import Kua

  kua = Kua(api_key=os.environ["KUA_API_KEY"])
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init("https://api.kuahr.com/v1/payroll/runs");
  curl_setopt_array($ch, [
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => ["X-Api-Key: " . getenv("KUA_API_KEY")],
  ]);
  $response = curl_exec($ch);
  ```
</CodeGroup>

<Warning>
  The full secret is shown **only once**, when the key is created. Store it in your
  secret manager immediately. If you lose it, rotate the key.
</Warning>

## Scopes

Keys carry **scopes** that gate what they can do. Read endpoints need no scope;
write endpoints require the matching scope:

| Scope               | Grants                                                         |
| ------------------- | -------------------------------------------------------------- |
| `employees:write`   | Create employees (`POST /v1/employees`)                        |
| `payroll:write`     | Create payroll runs (`POST /v1/payroll/runs`)                  |
| `leave:read`        | Read leave types, requests, and balances                       |
| `leave:write`       | Create/approve/reject/cancel leave, manage types & allocations |
| `advances:read`     | Read salary advances                                           |
| `advances:write`    | Request/approve/reject salary advances                         |
| `performance:read`  | Read objectives, appraisals, and promotions                    |
| `performance:write` | Manage objectives, appraisals, and promotions                  |
| `*`                 | All scopes                                                     |

A request with a key that lacks the required scope returns `403` with code
`INSUFFICIENT_SCOPE`.

<Note>
  The **add-on** scopes (`leave:*`, `advances:*`) also require the company to have
  that add-on **enabled**. If it isn't, those endpoints return `403`
  [`MODULE_NOT_ENABLED`](/errors) regardless of the key's scopes — a company admin
  enables it in **Settings → Modules**.
</Note>

## Rotating a key

Rotate from the dashboard (or `POST /api-keys/{id}/rotate`) to revoke the old
secret and mint a new one with the **same name and scopes** — so you can roll a
credential without reconfiguring the integration. The old secret stops working
immediately.

## Keeping keys safe

* Never embed a key in client-side code or commit it to source control. Keys
  begin with `kua_` so secret scanners can spot leaks.
* Use a separate key per integration so you can revoke one without affecting the
  others.
* Revoke unused keys from the dashboard.
