# Authentication

> Create and rotate mak_ API keys, give each one the scopes it needs, cap its spending, and let a browser poll with a pat_ token.

Every request sends one credential as `Authorization: Bearer <credential>` to
`https://api.mynth.io`. There is no `/v1` prefix. Which credential you send
depends on where the code runs:

| Where the code runs | Credential | Reaches                                                   |
| ------------------- | ---------- | --------------------------------------------------------- |
| Your server         | `mak_...`  | Every endpoint the key's scopes allow                     |
| A browser           | `pat_...`  | `GET /tasks/{id}/status` and `/result` for one task       |
| mynth.io            | OAuth      | The dashboard and playground. Your code never sends this. |

The API key is the only credential you create and store. The `pat_` token is
issued with each task. [The API reference](https://mynth.io/docs/api-reference/authentication.md)
has the header rules, the scope each endpoint needs, CORS, and every
authentication error.

## Create a key

**CLI**

```bash
npx @mynthio/cli auth login
npx @mynthio/cli api-key create my-app
```

**REST**

```bash
curl https://api.mynth.io/api-key \
  -X POST \
  -H "Authorization: Bearer $MYNTH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "my-app" }'
```

`POST /api-key` needs a key with the `keys` scope. In the
[dashboard](https://mynth.io/dashboard/keys/new), the new key appears once in a dialog, next to
a ready-made `MYNTH_API_KEY=mak_...` line.

The key is shown once. The create response carries it at `data.raw`, and no
endpoint returns it again. Mynth stores only an HMAC-SHA256 of the key, so a
lost key cannot be recovered. Lists show `keyPreview`, such as `mak_9d4...e3f`.

A key is `mak_` followed by 48 hex characters. An account can hold 100 live
keys. Deleted keys do not count toward that.

The SDK and the CLI both read `MYNTH_API_KEY`. In the CLI it takes precedence
over the key stored by `auth login`.

## Check a key

`GET /me` accepts any key and describes it. `npx @mynthio/cli whoami` prints
the same thing.

```bash
curl https://api.mynth.io/me -H "Authorization: Bearer $MYNTH_API_KEY"
```

```json
{
  "data": {
    "userId": "user_01JD8G3W1R5T6Y7U8I9O0P1Q2W",
    "auth": {
      "method": "api-key",
      "apiKey": {
        "id": "ak_01KE7XWWEQ4MCGWKBQKJ1G47RP",
        "name": "my-app",
        "keyPreview": "mak_9d4...e3f",
        "scopes": ["generate"],
        "spending": { "mode": "unlimited" }
      }
    }
  }
}
```

Run it first when a request fails with `401` or `403`.

## Scopes

A key carries one or more scopes. New keys get `generate` unless you ask for
more.

| Scope      | Reaches                                                                       |
| ---------- | ----------------------------------------------------------------------------- |
| `generate` | `/image/*`, `/video/*`, `/tasks/*`, `/me`                                     |
| `manage`   | `/webhook`, [`/destinations`](https://mynth.io/docs/concepts/destinations.md), `/balance`, `/me` |
| `keys`     | `/api-key`, `/me`                                                             |

Give a key only what its job needs. A leaked `generate` key can spend your
balance. It cannot create keys, read your balance, or point a webhook
somewhere else.

You can change a key's scopes later, on its page in the
[dashboard](https://mynth.io/dashboard/keys) or with `PUT /api-key/{id}`. The change applies
to the next request.

**An API key cannot grant `manage` or `keys`.** When the caller authenticates
with an API key, `POST /api-key` and `PUT /api-key/{id}` accept only
`["generate"]` and answer anything wider with `403 SCOPE_ESCALATION`. Create
or widen those keys in the dashboard. The key `auth login` creates is the
exception: it is minted through the browser approval and gets all three
scopes by default.

## Rotate a key

Create a second key, deploy it, then delete the first. Both keys work during
the switch. Deleting a key revokes it immediately.

## Spending limits

A key can carry a USD cap that resets each day, week, or calendar month. Set
it on the key's page in the dashboard, or send `spendingLimit` and
`spendingLimitPeriod` to `PUT /api-key/{id}`. The create call does not take
one.

The cap counts each task's estimate when the task is created. Once the next
estimate would pass the cap, creating a task fails with
`429 SPENDING_LIMIT_EXCEEDED` until the period rolls over. A task that fails
later does not give its estimate back to the cap. `GET /me` reports `used`,
`limit`, and `remaining` for a capped key.

## Browser polling

`POST /image/generate`, `POST /image/remove-background`, and
`POST /video/generate` return `data.access.publicAccessToken`, a `pat_` token
for that one task. It works only on `GET /tasks/{id}/status` and
`GET /tasks/{id}/result` for that task, and it expires one hour after it is
issued.

Create the task on your server, give the browser the task id and the token,
and poll from the page. [Poll for results](https://mynth.io/docs/guides/poll-for-results.md#from-a-browser)
has the code, and [the reference](https://mynth.io/docs/api-reference/authentication.md#public-access-tokens)
has every rule.

> **Warning**
>
> Never ship a `mak_` key to the browser. Only the two polling paths allow cross-origin requests, so
> a key in a page cannot generate from that page, but anyone can read it from the bundle and spend
> your balance from anywhere else.

## Next steps

- [Authentication reference](https://mynth.io/docs/api-reference/authentication.md): the header, per-endpoint scopes, CORS, and error codes.
- [Getting started](https://mynth.io/docs/getting-started.md): first key, first image.
- [Webhooks](https://mynth.io/docs/concepts/webhooks.md): signed with their own `wbs_` secret, separate from your key.
