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 runsCredentialReaches
Your servermak_...Every endpoint the key's scopes allow
A browserpat_...GET /tasks/{id}/status and /result for one task
mynth.ioOAuthThe 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 has the header rules, the scope each endpoint needs, CORS, and every authentication error.

Create a key#

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

POST /api-key needs a key with the keys scope. In the dashboard, 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.

curl https://api.mynth.io/me -H "Authorization: Bearer $MYNTH_API_KEY"
{
  "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.

ScopeReaches
generate/image/*, /video/*, /tasks/*, /me
manage/webhook, /destinations, /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 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 has the code, and the reference has every rule.

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#