Skip to main content

Authentication

Every management request carries an access token:

curl https://fileshub.zaions.com/api/public/v1/token \
-H "Authorization: Bearer fh_pat_XXXXXXXXXXXXXXXX"

The X-Access-Token: fh_pat_... header is accepted as a fallback when a bearer header is awkward to set. Prefer the bearer header — and never put a --token-style value where a shell records it.

Get a token

Create one in the FilesHub admin under Access Tokens: give it a name, decide whether it covers all projects or a chosen set, and optionally set an expiry. The full token is shown on the token's detail page (owner only) and is stored encrypted, so you can re-read it later — but treat it like a password.

Introspect first

An agent's first call should confirm the token works and learn its scope:

curl https://fileshub.zaions.com/api/public/v1/token -H "Authorization: Bearer $TOKEN"
{
"data": {
"name": "Claude Code CLI",
"token_prefix": "fh_pat_XzTJC7PU",
"all_projects": true,
"can_manage_supabase": true,
"can_read_vault": true,
"can_reveal_vault": false,
"can_write_vault": false,
"can_read_supabase_tokens": false,
"can_read_ai_accounts": false,
"can_read_developer_accounts": false,
"expires_at": null,
"last_used_at": "2026-07-18T09:12:44+00:00",
"created_at": "2026-07-18T09:00:00+00:00"
}
}

When the token is scoped to specific projects, all_projects is false and a projects array lists exactly what it may manage — id, public_id, name, slug per row.

projects is absent, not null, on an all-projects token

Note the response above has no projects key at all. An all-projects token covers projects that do not exist yet, so no list could describe it, and the field is omitted rather than sent as null or []. Test with 'projects' in data (or data.projects !== undefined), never data.projects === null. Every other field on this page is always present.

Four further booleans are separate, off-by-default axes, independent of the project scope:

FlagGates
can_manage_supabaseThe Supabase project vault — account-wide, so the project scope cannot express it
can_read_vaultThe project vault: metadata, links, and which credentials exist — never a value
can_read_developer_accountsDeveloper accounts — the tokens that publish under your name (npm, Hugging Face, Docker Hub, …). Its own axis: neither can_reveal_vault nor can_read_ai_accounts grants it
can_reveal_vaultThe project vault's credential values, config-file bytes and .env blocks. Implies read
can_read_supabase_tokensA Supabase account's personal access token (sbp_…) — the whole account, not one project
can_write_vaultWriting project credentials, and assigning accounts to projects. Implies read, not reveal — a caller populating a vault needs to see which fields are already set, and does not need every secret handed back
can_read_ai_accountsAn AI provider account's key — account-wide, so it spends that account's balance and reaches every model on it

Two imply can_read_vault: can_reveal_vault and can_write_vault. Nothing else implies anything — each remaining flag grants only itself. In particular:

  • can_manage_supabase does not grant can_read_supabase_tokens — one reaches a project's credentials, the other reaches the account that owns every project.
  • can_reveal_vault does not grant can_read_ai_accounts or can_read_developer_accounts. It is the strongest grant over one project's configuration, and neither an account-wide AI key nor a token that publishes packages under your name is that.
  • can_read_ai_accounts and can_read_developer_accounts do not imply each other. Spending an AI balance and publishing a package are separate decisions.

A token missing one of these gets 403 TOKEN_PERMISSION_DENIED with details.required_scope, rather than the anti-enumeration 404 used for resources — the scope is a property of your own token, so an honest error is more useful than pretending the resource does not exist. The exact strings that field returns are not uniform yet: details.required_scope.

Auth errors

Every failure is 401 with a machine-readable code:

CodeMeaning
MISSING_ACCESS_TOKENNo bearer / X-Access-Token header was sent.
INVALID_ACCESS_TOKEN_FORMATThe value is malformed — e.g. you sent an fh_live_ API key, which does not work here.
INVALID_ACCESS_TOKENThe token is well-formed but unknown.
TOKEN_REVOKEDThe token exists but is deactivated. Re-activate or mint a new one.
TOKEN_EXPIREDThe token is past its expiry.

Revoke

Turn a token inactive (or delete it) in the admin. The next request with it returns TOKEN_REVOKED immediately — there is no cache to wait out.

Rate limit

The management plane is limited to 120 requests per minute per client. Beyond that you get a standard 429.