# Authentication

> Key format, scope, integration binding, rotation.

Source: https://invoia.io/developers/api/authentication

Every request to `/api/v1` authenticates with a **Bearer API key**
minted in Settings → Developers.

```
Authorization: Bearer ivk_LiveKey0123456789abcdefghij…
```

## Key format [#key-format]

Keys look like `ivk_<random>` — no environment segment (there is no
sandbox to distinguish). The random suffix carries the full entropy of
the secret.

Invoia stores only the SHA-256 hash of the key plus a **prefix** and the
**last four characters** for display. If you lose the raw secret, it
cannot be recovered — revoke the key and mint a new one.

## Scope [#scope]

Each key is created with one of two scopes:

* **`read`** — every `GET` endpoint. Use for dashboards and one-way
  read-out sync.
* **`read_write`** — everything a `read` key can do plus `POST`, `PATCH`,
  `PUT`, `DELETE`. Use for your primary sync job.

Trying to write with a `read` key returns:

```json
HTTP/1.1 403 Forbidden
{
  "error": {
    "type": "forbidden",
    "code": "INSUFFICIENT_SCOPE",
    "message": "This key does not have write access.",
    "request_id": "req_…"
  }
}
```

## Integration binding [#integration-binding]

A key is bound to **one connected Dinero integration** — the account's
integration is baked into the key at mint time. The API never accepts
an `integration_id` in the URL or body: your key IS the integration.
This means a compromised or misconfigured key can never write to the
wrong org.

If you operate several integrations (e.g. multiple entities), mint one
key per integration and route requests in your client.

## Rotation [#rotation]

The recommended zero-downtime rotation:

1. Mint a new key alongside the current one (multiple active keys per
   integration are supported).
2. Deploy your integration with the new key.
3. Verify the new key's `last_used_at` is advancing in Settings →
   Developers.
4. Revoke the old key.

Revocation is immediate: the next request the revoked key makes returns
`401 UNAUTHENTICATED`.

## What the API cannot do [#what-the-api-cannot-do]

Deliberately outside the API:

* Invite, deactivate, or change roles of users.
* Change account billing.
* Connect a Dinero organisation (an OAuth browser flow that stays in the
  UI).

These stay in the Invoia UI where an admin can perform them from a
signed-in session.

## Never expose the key to a browser [#never-expose-the-key-to-a-browser]

The credential is a server-to-server admin key. There is no CORS
allowance; do not embed the key in a frontend bundle or send it from a
customer's browser. Every call is audit-logged with the key id and a
`request_id`.
