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

Client tokens

Your backend mints short-lived client tokens with its API key so apps never hold the key. How authentication works describes the endpoint your backend adds: mint only for a signed-in user, and take end_user_id from that user's session, never from the request.

Client-token scopes

A client token can call only the operations its scopes allow; anything else fails with 403 scope_insufficient. An app's January client uses one token for every call, so request the scopes of every feature your app uses, and no others.

Scope
Operations

foods:read

Foods: autocomplete, search, barcode lookup, get a food, and alternatives

restaurants:read

Restaurants: restaurant search, menu-item search, and a restaurant's menu

food_analysis:write

Food analysis: image, text, and corrections

food_logs:read

Food logs: list, summary, and get

food_logs:write

Food logs: create, update, and delete

water_logs:read

Water logs: list daily totals

water_logs:write

Water logs: create and delete

weight_logs:read

Weight logs: list daily weights

weight_logs:write

Weight logs: create

glucose:read

Minting and revoking client tokens, and reading Credits, accept only an API key.

Each operation's description states what it costs — see Credits and pricing.

Mint a client token

post

API key only.

Never consumes credits.

Exchanges your partner API key for a short-lived token bound to one of your end users, so your mobile app can call the v1.2 API directly instead of through a proxy that holds your key.

This endpoint requires your API key (sk-…), so always call it from your backend — behind whatever login already protects your own APIs. Never ship your API key in a mobile app in order to call this from the device: that puts a credential for your whole account in every copy of your app, which is the problem client tokens exist to solve.

The end user is identified by end_user_id in the body, not the January-End-User-ID header: the token is bound to it, and requests made with the token act only on that user, whatever headers they carry.

Tokens last 300–7200 seconds and the raw value is returned exactly once — it is stored only as a hash, so it can never be retrieved again. Relay it to the device and let the device refresh when it expires: a 401 with code token_expired is the signal to mint a new one and retry the original request once.

Authorizations
AuthorizationstringRequired

Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.

API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.

Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.

Body
end_user_idstring · max: 64Required

Your stable ID for the end user this token acts as. The token is bound to it; requests made with the token act only on this user.

Example: acme-user-8271
ttl_secondsinteger · min: 300 · max: 7200Optional

How long the token stays valid, in seconds. Between 300 and 7200; defaults to 1800.

Default: 1800Example: 1800
Responses
201

Token minted. The raw token is returned exactly once — relay it to the device and store nothing; only its hash is kept. The response also echoes expires_in/expires_at, the bound end_user_id, and the exact scopes granted.

application/json
tokenstringRequired

The credential itself. Shown exactly once — it is stored only as a hash and can never be retrieved again.

Example: ct-4fQr7yNb2KcXm9TvLpZ3wHs6JdRg8AeYuQ1oViB5xCn
expires_inintegerRequired

Seconds until the token expires, counted from the moment this response was produced. Compute expiry from this rather than from expires_at — a device clock that is wrong makes an absolute timestamp wrong with it.

Example: 1800
expires_atstring · date-timeRequired

The same expiry as an absolute UTC instant, for logs and humans.

Example: 2026-08-26T18:35:11.000Z
end_user_idstringRequired

The end user this token is bound to, echoed back so a caller can assert it minted what it meant to.

Example: acme-user-8271
post/v1.2/auth/client-tokens
POST /v1.2/auth/client-tokens HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 75

{
  "end_user_id": "acme-user-8271",
  "scopes": [
    "foods:read"
  ],
  "ttl_seconds": 1800
}
{
  "token": "ct-4fQr7yNb2KcXm9TvLpZ3wHs6JdRg8AeYuQ1oViB5xCn",
  "expires_in": 1800,
  "expires_at": "2026-08-26T18:35:11.000Z",
  "end_user_id": "acme-user-8271",
  "scopes": [
    "foods:read",
    "food_logs:read"
  ]
}

Revoke an end user’s client tokens

post

API key only.

Never consumes credits.

Revokes every outstanding client token for one end user. Safe to call repeatedly: it reports how many tokens it actually stopped, so an immediate second call reports 0.

This endpoint requires your API key (sk-…), so always call it from your backend. A client token cannot revoke anything, its own included.

Revocation takes effect within 60 seconds, the authentication cache window. For an immediate cut-off, stop trusting the user in your own app as well — and note that a token minted in the same instant as the revoke may survive it, bounded by its own expiry.

If some tokens cannot be revoked, the call answers 503 with code client_token_revocation_incomplete, naming how many of them succeeded rather than reporting a smaller count as though it were the whole story — retry until it succeeds, which only picks up the remainder.

One call stops at most 500 tokens so it cannot time out, so repeat the call until revoked_count is 0 if a user is somehow holding more than that (which means your app is minting per request rather than per session).

Authorizations
AuthorizationstringRequired

Your credential, sent as Authorization: Bearer <credential>. Two kinds reach this API, and the prefix tells them apart.

API key (sk-…) — created in the Developer Dashboard, it authenticates your whole account. Use it only where you control the machine — never inside a mobile app or a browser. Every endpoint accepts it.

Client token (ct-…) — a short-lived credential bound to one of your end users, minted from your API key by POST /v1.2/auth/client-tokens on your backend and relayed to the device. Safe to hold on a phone: it expires within 2 hours and acts only as the user it was minted for. Endpoints that open with API key or client token accept one and name the scope it must carry; endpoints that open with API key only refuse it.

Body
end_user_idstring · max: 64Required

The end user whose outstanding client tokens should all be revoked.

Example: acme-user-8271
Responses
200

Revocation processed. revoked_count is how many live tokens this call actually stopped — repeat the call until it reports 0, since already-revoked and already-expired tokens are not counted.

application/json
revoked_countintegerRequired

How many live tokens this call revoked. Already-revoked and already-expired tokens are not counted, so an immediate second call reports 0 — repeat the call until it does.

Example: 2
post/v1.2/auth/client-token-revocations
POST /v1.2/auth/client-token-revocations HTTP/1.1
Host: partners.january.ai
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 32

{
  "end_user_id": "acme-user-8271"
}
{
  "revoked_count": 2
}

Last updated

Was this helpful?