# Authentication

> Key format, scope, the company a key is bound to, rotation.

Source: https://www.tabba.io/developers/api/authentication

Every request to `/api/v1` authenticates with an **API key**, sent as a bearer
token. An admin or a bookkeeper of the company mints it in the company's settings,
under Developers (`/workspace/settings/company/developers`).

```
Authorization: Bearer tba_…
```

The header is the only credential the API reads. A session cookie is never
read.

## Key format [#key-format]

A key is `tba_` followed by 32 URL-safe characters. There is no environment
segment, because there is no sandbox to tell apart.

The raw key is shown once, when it is minted. Tabba stores only its SHA-256
hash, and the Developers page shows a key by its prefix and its last four
characters. A lost key cannot be recovered: revoke it and mint a new one.

## Scope [#scope]

A key is minted with one of two scopes:

* **`read`**: every `GET` endpoint. For dashboards and a one-way sync out of
  Tabba.
* **`read_write`**: everything a `read` key does, and every `POST`, `PUT`,
  `PATCH` and `DELETE`.

A `read` key on a write endpoint is refused:

```json
HTTP/1.1 403 Forbidden
{
  "error": {
    "type": "permission_error",
    "code": "SCOPE_INSUFFICIENT",
    "message": "This endpoint needs a read_write key; this key is read.",
    "request_id": "req_…"
  }
}
```

The refusal comes before the rate limit is counted, so it spends nothing of
the key's budget.

## One key, one company [#one-key-one-company]

A key is bound to **one connected company** when it is minted, and it can be
minted only for a company that is connected. No URL and no body of the API
names a company: the key does. `GET /config` answers which one, as
`integration_id` and `company_name`.

A record of any other company does not exist as far as the request can tell:
a path that names one is answered `404` with `NOT_FOUND`.

If you operate several companies, mint one key for each and route requests in
your client.

**Disconnecting a company revokes its keys.** A revocation is final, so after
the company is connected again an admin or a bookkeeper mints a new key.

## Refused keys [#refused-keys]

| Request                                     | Status | Code              |
| ------------------------------------------- | ------ | ----------------- |
| No `Authorization: Bearer` header           | `401`  | `UNAUTHORIZED`    |
| A key that is malformed, unknown or revoked | `401`  | `INVALID_API_KEY` |

A revoked key and an unknown key are answered alike. Revocation takes effect
at once: the next request under the key is refused.

Requests that carry no key of Tabba's are counted per client address and are
answered `429` with `RATE_LIMITED` past their limit. A request under your live
key is not counted there. See [Errors](/developers/api/errors).

## Rotation [#rotation]

To rotate a key with no downtime:

1. Mint a new key for the same company beside the current one. A company can
   have several live keys.
2. Deploy your integration with the new key.
3. Check on the Developers page that the new key's last use is advancing.
4. Revoke the old key.

## What the API does not do [#what-the-api-does-not-do]

These stay in the workspace, where an admin does them from a signed-in
session:

* Inviting and removing people, and changing a person's role.
* Connecting and disconnecting an accounting system.
* Minting and revoking API keys.
* Registering a webhook endpoint, rotating its signing secret and replaying a
  delivery.

## Keep the key on a server [#keep-the-key-on-a-server]

The key acts for the whole company within its scope. The API sends no CORS
header: it is called by servers. Do not embed a key in a frontend bundle or
send it from a browser.

Every call under a key is written to the account's activity log with the key
and the `request_id` of the call. A registration of work is credited to the
person the request names, since a key is not a person.
