For the complete documentation index, see llms.txt. This page is also available as Markdown.

How authentication works

January accepts two credentials. Both travel in the same header, Authorization: Bearer …:

Credential
Held by
What it can do

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:

  1. The app asks your backend for a token. The request goes to an endpoint you add, authenticated like the rest of your API.

  2. Your backend mints the token with its API key, for the user the session proves.

  3. January returns a ct-… token bound to that user, with expires_in in seconds.

  4. Your backend returns January's JSON to the app unchanged.

  5. The app calls January directly with Authorization: Bearer ct-…. On 401 token_expired it 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.

  1. Authenticate the request with your existing app session. Return 401 if there is none.

  2. 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-ID header.

  3. Mint a token with your API key:

    scopes is 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 with 403 scope_insufficient. ttl_seconds is optional: 300–7200, default 1800.

  4. Return January's 201 body unchanged, with Cache-Control: no-store:

    The iOS, Android, and Web SDKs read token and expires_in from this body, and also accept expiresIn. The React Native SDK reads expiresIn, so its token provider maps the field; see React Native authentication.

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-token from the iOS Simulator, a browser, or a desktop app.

  • http://10.0.2.2:8787/api/january/client-token from the Android emulator.

Send a POST. A GET returns a status page, not a token.

Token relay (development)
Your endpoint (production)

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?