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.
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:
| Prefix | Credential | Checked as |
|---|---|---|
mak_ | API key | A live key on your account |
pat_ | Public access token | A signed token for one task |
| any other | OAuth access token | A 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, with a message that API keys start with mak_. Send
the key exactly as it was shown.
| Credential | Format | Issued by | Lifetime | Reaches |
|---|---|---|---|---|
| API key | mak_ + 48 hex chars | You: dashboard, CLI, or POST /api-keys | Until you delete it | Every endpoint its scopes allow |
| Public access token | pat_ + a signed JWT | Mynth, in a task's create response, on request | 1 hour | /status and /result of that one task |
| OAuth access token | JWT | mynth.io when you sign in | Your mynth.io session | The dashboard and playground |
An API key needs one of the scopes listed for the endpoint. OAuth sessions pass every scope check.
| Endpoint | API key scope | pat_ |
|---|---|---|
POST /image/*, including /estimate and /upload | generate | No |
POST /video/*, including /estimate | generate | No |
GET /tasks, GET /tasks/{id} | generate | No |
GET /tasks/{id}/status, GET /tasks/{id}/result | generate | Yes, its own task |
/webhooks, /destinations, GET /balance | manage | No |
/api-keys | keys | No |
GET /me | any of generate, manage, keys | No |
GET /models, GET /health | none, public | n/a |
A key without the scope gets 403, and the body says what was missing:
{
"error": {
"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 error.scopes.required rather than parsing message. Scopes can be
changed on an existing key in the dashboard, or with PUT /api-keys/{id}.
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.404 task_not_found, never 403, so the API
never confirms that an id exists.POST /image/generate, POST /image/remove-background,
POST /image/upscale, and POST /video/generate return a token for the task
they create when the body asks for one with
"generate_public_access_token": true:
{
"data": {
"task_id": "tsk_01KE7XWWEQ4MCGWKBQKJ1G47RP",
"estimated_cost": "0.03",
"public_access_token": "pat_eyJhbGciOiJIUzI1NiJ9..."
}
}GET /tasks/{id}/status and
GET /tasks/{id}/result for that task. Every other endpoint rejects it.generate_public_access_token: true there is no token. Ask for one
only when browser code will poll the task.500 public_access_token_failed with the task's id in
error.task_id. The task runs: follow it from your server rather than
resending, which would create and charge a second task.POST /image/rate, /image/alt, and /image/review never return one.Poll for results has the pattern end to end.
| Paths | Allowed origins |
|---|---|
GET /tasks/{id}/status, GET /tasks/{id}/result | Any |
| Everything else | https://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.
| Status | Code | When |
|---|---|---|
| 401 | unauthorized | No usable header, or a key that is unknown or deleted. message says which: a missing header, a header without Bearer, a pat_ where a key is needed, or a key that is not valid |
| 401 | invalid_token | A pat_ that is malformed or not signed by Mynth |
| 401 | token_expired | A pat_ past its hour |
| 403 | insufficient_scope | A valid key without a scope the endpoint accepts |
| 403 | scope_escalation | An API key tried to create or edit a key with manage or keys |
| 404 | task_not_found | Another 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.