Every request to /v1 carries a bearer token. There is one header form and no query-string alternative.
Authorization: Bearer upfynai-sk-...
API keys#
A key is the string upfynai-sk- followed by 32 hex characters. We store a hash of it, never the key, which has one consequence worth stating plainly: the full key is returned exactly once, when you create it. After that every listing shows only a preview like upfynai-sk-...4f31.
Creating a key
curl https://ai.upfyn.com/v1/me/keys \-H "Authorization: Bearer $UPFYN_API_KEY" \-H "Content-Type: application/json" \-d '{ "name": "production-worker", "scopes": ["gateway:inference"] }'
{"id": "ak_7c19d4a2f0b34e6d81af52c9e7d03b1a","key": "upfynai-sk-9f3c1d70a84b42e5b1c6a7d208e94f31","user_id": "usr_...","name": "production-worker","scopes": ["gateway:inference"],"key_type": "user","app_id": null}
| Field | Type | Required | Notes |
|---|---|---|---|
| name | string | — | A label for you. Name it after the thing that will use it. |
| scopes | string[] | — | Defaults to gateway:full if omitted or empty. See below. |
| app_id | string | — | Attributes usage to one of your developer apps. |
| expires_at | string | — | ISO timestamp. The key stops working after it. |
Treat a key like a password
A key spends your credits. Keep it on a server you control, in an environment variable. Never ship one in a browser bundle, a mobile binary, or a public repository. If one leaks, revoke it from API Keys immediately — revocation takes effect on the next request.Scopes#
Scopes narrow what a key may do. Give a key the least it needs: a worker that only runs inference has no business reading your billing history.
| Scope | Grants |
|---|---|
gateway:inference | Chat, tools, images, audio, video — every model endpoint. |
profile:read | Read your account and workspace. |
profile:write | Update your profile and rename your workspace. |
usage:read | Usage, logs and session history. |
billing:read | Purchases and the credit ledger. |
gateway:full | Everything, including creating and deleting sessions. The default. |
Scopes on API keys are narrower than they look
Scope checks on the account endpoints (/v1/me/*) currently apply to OAuth tokens only. An API key with just gateway:inference is still blocked from inference it lacks — but it is not blocked from reading /v1/me. Do not rely on a narrow scope as a hard boundary against your own account data; rely on it for inference control, and treat every key as account-level for reads.Other credentials#
The same header accepts three other kinds of token, tried in order. You are unlikely to need any of them directly.
- OAuth access tokens — issued to a developer app acting for a user. These do get strict scope enforcement, and they cannot manage API keys.
- Shared session tokens — minted by Upfyn sign-in and used by our own apps.
- Session cookie — when there is no
Authorizationheader, the dashboard’s own cookie is used. Browser only.
There is no x-api-key header
Some providers accept a key in a custom header or a query parameter. This one does not. A request withoutAuthorization: Bearer … is a 401.What failure looks like#
| Status | code | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing, malformed, revoked or expired. The only 401 in the auth path. |
| 403 | insufficient_scope | Valid key, but it lacks the scope this endpoint needs. |
| 403 | account_blocked | The account is suspended. Contact support. |
| 403 | api_access_disabled | API access is turned off for this account. |
| 402 | insufficient_credits | Authentication was fine; you are out of credits. See Credits. |
A 402 is not an auth failure and should not trigger a re-auth. Retrying it without adding credits will fail identically.
