Authorization: Bearer <token> on every other request.
Four kinds of credential
They are not interchangeable, and using the wrong one is the most common integration mistake.JWT, 8 hours
What
POST /api/auth/login and the CLI device flow return. Identifies a person. Carries their
role, so an admin’s token opens admin endpoints. This is the one you want for scripts that act on
your own behalf.30 days
Issued alongside the access token, and only ever as an
httpOnly cookie — never in a response
body, so browser JavaScript cannot read it. POST /api/auth/refresh exchanges it for a new access
token. A command-line client re-runs the device flow instead.30 days, one run
Injected into a training container as
STARFORGE_TOKEN. Scoped to a single run and only accepted
by /api/ingest/*. A job holds this and nothing else — it never sees your account credentials.
See Reporting from training code.revocable, no expiry
Authorizes application traffic to one model deployment and nothing
else. Independent of any login session, so a service keeps working after the person who created it
leaves. Revoke it from the console.
Roles are
admin and operator (the console labels the latter “Member”). Admin-only endpoints
return 403 for an operator token — not 401, which
would wrongly suggest the token is bad.Log in from a browser or a script
GET /api/auth/config to see which login methods a deployment offers before assuming.
Log in from a machine with no browser
The device flow, RFC 8628. This is whatsf login --device-flow uses, and what you want on an SSH
session or a build agent.
1
Ask for a code
2
Approve it in a browser
Open
verification_uri_complete anywhere you are already signed in, on any device.3
Poll until it is approved
authorization_pending until somebody approves it, then the access token and
expires_in. Poll no faster than every five seconds; twenty polls per minute is the ceiling.Confirm it worked
401 here means the token is absent, malformed, or
expired — try logging in again before looking anywhere else.
Handling expiry
An access token lasts eight hours by default (FORGE_JWT_HOURS on the deployment). When one
expires, endpoints return 401. Two ways to recover, depending on what you are:
- A browser session:
POST /api/auth/refreshwith the refresh cookie. - A script or CI job: log in again, or re-run the device flow. Do not cache a token across runs
and hope; check
GET /api/auth/meand re-authenticate on401.
POST /api/auth/logout revokes the refresh token and, on deployments with Redis configured,
invalidates the access token immediately rather than at expiry.
Why the refresh token never appears in a response body
Why the refresh token never appears in a response body
A token that JavaScript can read is a token an injected script can steal, and a 30-day refresh
token is worth far more than an 8-hour access token. Keeping it in an
httpOnly cookie means an
XSS bug costs a session rather than a month of access. It also means a command-line client cannot
use it at all — which is why the device flow exists as a separate path rather than an afterthought.