How authentication works
January accepts two credentials. Both travel in the same header, Authorization: Bearer …:
API key (sk-…)
Your backend
Call every endpoint for your account.
Client token (ct-…)
Your app, for one signed-in user
Act as that one user, within the scopes it was granted, for 5 minutes to 2 hours (30 minutes by default).
These terms are used throughout the docs:
End user: a person using your app.
End-user ID: your own stable, opaque ID for that person, up to 64 characters (for example
acme-user-8271). Never use an email address, a name, or other personal data. There is no registration call; any stable ID you choose works.App session: the login token or cookie between your app and your backend. January never sees it.
API keys
Create a key in the January Developer Dashboard under API keys → Create key. The full value is shown once. Keep it in a secret manager or a server environment variable. Never put it in a browser bundle, a mobile app, a repository, a log, or a screenshot.
Client tokens
An app on an end user's device never holds the API key. It asks your backend for a client token, then calls January directly with it:
The app asks your backend for a token. The request goes to an endpoint you add, authenticated like the rest of your API.
Your backend mints the token with its API key, for the user the session proves.
January returns a
ct-…token bound to that user, withexpires_inin seconds.Your backend returns January's JSON to the app unchanged.
The app calls January directly with
Authorization: Bearer ct-…. On401 token_expiredit gets a new token and retries once. The SDKs do this for you.
Mobile apps can always call January directly. Browser apps can do so only from web origins January has enabled for your account; until then, call January from your backend. See Web SDK runtime boundaries.
Your token endpoint
The token endpoint is the only server code a client-token integration needs, and it is the same for every SDK.
Authenticate the request with your existing app session. Return
401if there is none.Look up the end-user ID for that session in your own records. Never take it from the request: not from the body, the query, or a
January-End-User-IDheader.Mint a token with your API key:
scopesis required. The app's January client uses one token for every call, so list the scopes of every feature your app uses; see the scope table. A call outside the token's scopes fails with403 scope_insufficient.ttl_secondsis optional: 300–7200, default 1800.Return January's
201body unchanged, withCache-Control: no-store:The iOS, Android, and Web SDKs read
tokenandexpires_infrom this body, and also acceptexpiresIn. The React Native SDK readsexpiresIn, so its token provider maps the field; see React Native authentication.
Minting fails with 403 forbidden until Enable client tokens is switched on in the Developer Dashboard → Client tokens.
Keep the endpoint safe:
Mint a new token each time the app asks, and don't store tokens on your server. The SDKs cache the token in memory and ask again about 60 seconds before it expires.
Never log API keys or client tokens, and keep token responses out of analytics and crash reports.
Serve the endpoint over HTTPS and rate-limit it like the rest of your API.
To cut a user off, for example after account deletion, revoke their tokens with
POST /v1.2/auth/client-token-revocations. Revocation takes effect within 60 seconds.
Develop with the token relay
Until your endpoint exists, the January Token Relay can stand in for it on your computer. Clone it, run ./start.sh, and paste your API key when asked. Then point your app's token provider at:
http://localhost:8787/api/january/client-tokenfrom the iOS Simulator, a browser, or a desktop app.http://10.0.2.2:8787/api/january/client-tokenfrom the Android emulator.
Send a POST. A GET returns a status page, not a token.
Whose token it mints
The user named in the January-End-User-ID header the app sends
The user your session proves; request headers are ignored
How the app authenticates
Nothing on your own computer; a relay token when other devices can reach it
Your normal app session
Scopes
All ten (set TOKEN_SCOPES to narrow them)
Only the ones your app uses
Response
January's body, unchanged
January's body, unchanged
The token-provider samples in each SDK guide send both the app session and January-End-User-ID, so the same code works against the relay and against your endpoint. Switching over changes only the URL and the session credential. Your production endpoint must ignore the header.
See Client tokens for the scope each operation needs and the minting and revocation endpoints.
Last updated
Was this helpful?