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:
| 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. Send the key exactly as it was shown.
Credentials#
| Credential | Format | Issued by | Lifetime | Reaches |
|---|---|---|---|---|
| API key | mak_ + 48 hex chars | You: dashboard, CLI, or POST /api-key | Until you delete it | Every endpoint its scopes allow |
| Public access token | pat_ + a signed JWT | Mynth, in a task's create response | 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 |
Scope per endpoint#
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 |
/webhook, /destinations, GET /balance | manage | No |
/api-key | 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:
{
"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 /taskslists only the tasks the calling key created. An OAuth session lists every task on the account.GET /tasks/{id},/status, and/resultcheck the account, not the key. Any key on your account withgeneratecan read any of your tasks by id.- Another account's task answers
404 TASK_NOT_FOUND, never403, 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}/statusandGET /tasks/{id}/resultfor 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.accessas optional even when enabled. If signing fails, the task is still created and the field is missing. POST /image/rate,/image/alt, and/image/reviewnever return one.
Poll for results has the pattern end to end.
CORS#
| 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.
Failures#
| Status | Code | When |
|---|---|---|
| 401 | UNAUTHORIZED | No usable header, or a key that is unknown or deleted |
| 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.
Next steps#
- Authentication: create a key, set scopes, rotate, cap spending.
- The task object: what the task endpoints return.
- Errors: every code the API can send.