# Apps and keys

Every key belongs to an app. An app is a name for one integration, such as "Acme estimator" or "Nightly sync". A
company can have up to 25 apps. Apps and keys are free on every plan, Free included.

## Make an app

An admin opens **Settings → Developer → Apps → New app** and fills in:

- **Name** (up to 80 characters), and optionally a **Description** (up to 500), **Homepage** and **Support email**.
- **Scopes**: what the app may do. Any scope except `admin:org`. See [Scopes](https://developers.exayard.com/concepts/scopes.md).

The app works in your own company at once. Letting other companies install it is a separate, optional step; see
[Let other companies install your app](https://developers.exayard.com/authentication.md#let-other-companies-install-your-app).

## Make a key

In the app, under **Keys**, an admin clicks **New key** and picks:

- **Name**: 1 to 80 characters, such as "Production" or "Laptop".
- **Scopes**: **All** of the app's scopes (the default), or **Specific** ones. A key can never have more than its app.
- **Works in**: shown only once another company has the app installed. See [Company scope](#company-scope).
- **Expires**: optional. A key set to expire on a day works through the end of that day.

The key is shown once. Copy it then. An app can have **25 active keys**. A 26th is refused with
`app_key_limit_reached` until you revoke one. An expired key still counts until it is revoked.

## Key format

```
exa_live_<40 letters and digits><6-character checksum>
exa_test_<40 letters and digits><6-character checksum>
```

A key is 55 characters and matches `^exa_(live|test)_[0-9A-Za-z]{46}$`. The last 6 characters are a checksum, so a
typo is caught before any lookup. The Developer page shows a key by its start and its last 4 characters.

- `exa_test_`: the app belongs to a [sandbox](https://developers.exayard.com/environments.md).
- `exa_live_`: every other key, including keys of your app in a sandbox where it is installed.

Send the key as a bearer token:

```bash
curl https://api.exayard.com/v1/projects \
  -H "Authorization: Bearer exa_live_..."
```

## Company scope

**This company** is the default. The key works only in the app's own company, and it implies that company: leave out
`organizationId` and the `Exayard-Organization-Id` header. If a call names another company, or the header and the body
name two different ones, it is refused with 400 [`company_mismatch`](https://developers.exayard.com/concepts/errors.md#company_mismatch).

**Every company that installed it** appears once your app is installed in another company. Such a key works in each
company that installed the app, so every call must name its company with the `Exayard-Organization-Id` header (or
`organizationId`). A call that names none is refused with 400
[`company_required`](https://developers.exayard.com/concepts/errors.md#company_required), except `GET /v1/me`, `GET /v1/installations` and the
[`/v1/organizations`](https://developers.exayard.com/concepts/customer-accounts.md) calls.

## Expiry and revoking

- A key past its expiry is refused with 401 [`api_key_expired`](https://developers.exayard.com/concepts/errors.md#api_key_expired). An admin can make
  a new one.
- **Revoke** in the key's menu stops it. Allow up to 30 seconds for every server to stop accepting it. After that,
  calls get 401 `unauthenticated`.
- **Rename** changes only the name. The key itself stays the same.
- To swap keys with no gap, make a second key, move your servers to it, then revoke the first.
- Deleting an app revokes all of its keys.

Each key shows when it was last used. That time is updated at most once a minute.

## Rate limits per key

A key that works in its own company gets **60 calls a minute** of its own, within the app's ceiling of **600 calls a
minute** across all its keys and companies. When the key's own limit refuses a call, the 429 says
`rate_limit_scope: "app_key"`. See [Rate limits](https://developers.exayard.com/concepts/rate-limits.md).

## Older keys

- **API keys starting `ak_`** keep working. A company `ak_` key implies its company too. No new ones can be made. They
  are listed under **Legacy keys** on the Developer page, where an admin can revoke them.
- **App secrets starting `xa_app_`** keep working, and each counts as one of its app's 25 keys. No new ones can be
  made.

Move to an `exa_` key when it suits you. Nothing else in your code changes.

## Security

- **We keep only a hash.** Exayard stores the SHA-256 hash of each key, never the key. That is why it is shown once. A
  lost key cannot be recovered; make a new one and revoke the old one.
- **Keep keys on your server.** Never put a key in a browser, a mobile app or a public repository. Read it from an
  environment variable, such as `EXAYARD_API_KEY`, which the SDK and the CLI read by default.
- **Give each key only what it needs.** Pick specific scopes, set an expiry for short-lived work, and use one key per
  server or person so you can revoke one without stopping the others.
- **If a key leaks, revoke it.** Revoke it on the Developer page, then make a new one. The Developer page Logs show
  each call by app and key, so you can check what the leaked key did.

Every key has a fixed start (`exa_live_` or `exa_test_`) and a checksum, so secret scanners can recognise it. We are
joining GitHub's secret scanning program. Once that is live, a key pushed to a public GitHub repository is turned off
at once, and the admins of the app's company (up to 10) get an email with the subject "An Exayard API key was found
in public and turned off". A key that is already revoked gets no second email.
