Authentication

The Authorization header, the three credential types, the scope each endpoint needs, public access token rules, CORS, and authentication errors.

This page is the contract: what the header looks like, what each credential reaches, and what comes back when it cannot. Creating, rotating, and capping keys is on authentication.

The header#

Every authenticated request sends exactly one bearer token:

Authorization: Bearer <credential>

The scheme is case-insensitive. The value must be the scheme, whitespace, and the token, with nothing after it. Anything else counts as no credential. There is no query parameter, no X-API-Key header, and no Basic auth.

The API decides what the token is from its prefix:

PrefixCredentialChecked as
mak_API keyA live key on your account
pat_Public access tokenA signed token for one task
any otherOAuth access tokenA mynth.io session. Your code never sends one

A key pasted without its mak_ prefix is checked as an OAuth token and fails with 401 UNAUTHORIZED. Send the key exactly as it was shown.

Credentials#

CredentialFormatIssued byLifetimeReaches
API keymak_ + 48 hex charsYou: dashboard, CLI, or POST /api-keyUntil you delete itEvery endpoint its scopes allow
Public access tokenpat_ + a signed JWTMynth, in a task's create response1 hour/status and /result of that one task
OAuth access tokenJWTmynth.io when you sign inYour mynth.io sessionThe dashboard and playground

Scope per endpoint#

An API key needs one of the scopes listed for the endpoint. OAuth sessions pass every scope check.

EndpointAPI key scopepat_
POST /image/*, including /estimate and /uploadgenerateNo
POST /video/*, including /estimategenerateNo
GET /tasks, GET /tasks/{id}generateNo
GET /tasks/{id}/status, GET /tasks/{id}/resultgenerateYes, its own task
/webhook, /destinations, GET /balancemanageNo
/api-keykeysNo
GET /meany of generate, manage, keysNo
GET /models, GET /healthnone, publicn/a

A key without the scope gets 403, and the body says what was missing:

{
  "code": "INSUFFICIENT_SCOPE",
  "message": "This endpoint requires the `manage` scope. This key has: generate. Add the scope to this key in the dashboard, or use a key that has it.",
  "scopes": { "required": ["manage"], "current": ["generate"] }
}

Read scopes.required rather than parsing message. Scopes can be changed on an existing key in the dashboard, or with PUT /api-key/{id}.

Which tasks a key can read#

  • GET /tasks lists only the tasks the calling key created. An OAuth session lists every task on the account.
  • GET /tasks/{id}, /status, and /result check the account, not the key. Any key on your account with generate can read any of your tasks by id.
  • Another account's task answers 404 TASK_NOT_FOUND, never 403, so the API never confirms that an id exists.

Public access tokens#

POST /image/generate, POST /image/remove-background, and POST /video/generate return a token for the task they create:

{
  "data": {
    "taskId": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
    "estimatedCost": "0.03",
    "access": { "publicAccessToken": "pat_eyJhbGciOiJIUzI1NiJ9..." }
  }
}
  • It works as a bearer token on GET /tasks/{id}/status and GET /tasks/{id}/result for that task. Every other endpoint rejects it.
  • It expires one hour after it is issued, and nothing refreshes it.
  • Send "access": { "pat": { "enabled": false } } in the create body to skip it.
  • Treat data.access as optional even when enabled. If signing fails, the task is still created and the field is missing.
  • POST /image/rate, /image/alt, and /image/review never return one.

Poll for results has the pattern end to end.

CORS#

PathsAllowed origins
GET /tasks/{id}/status, GET /tasks/{id}/resultAny
Everything elsehttps://mynth.io and its subdomains

The allowed request headers are Authorization and Content-Type. Credentials mode is off, so cookies are never sent. A browser call to /image/generate from your own origin fails the preflight, whatever it sends.

Failures#

StatusCodeWhen
401UNAUTHORIZEDNo usable header, or a key that is unknown or deleted
401INVALID_TOKENA pat_ that is malformed or not signed by Mynth
401TOKEN_EXPIREDA pat_ past its hour
403INSUFFICIENT_SCOPEA valid key without a scope the endpoint accepts
403SCOPE_ESCALATIONAn API key tried to create or edit a key with manage or keys
404TASK_NOT_FOUNDAnother account's task, or a pat_ for a different task

/status and /result treat a missing or invalid API key as no credential and answer 404 TASK_NOT_FOUND instead of 401, so an unauthenticated caller cannot learn whether a task id exists. If polling a task you just created returns 404, check the header before the id.

A pat_ problem is never a 404 on those two paths. A malformed or expired token fails with its own 401 first.

Every code, with what to do about it, is on errors.

Next steps#