Reference

Token Claims Reference

Complete reference for AuthPI JWT claims: access, ID, and refresh token payloads, organization claims, aud precedence rules, and TTL configuration.

Last updated 2026-06-20

AuthPI issues access tokens, ID tokens, and refresh tokens as signed JWTs. All are verifiable against the issuer’s JWKS endpoint ({AUTHPI_ISSUER_URL}/jwks.json). The JWT header contains alg (ES256, RS256, or EdDSA, per the client’s response_signature_alg) and kid; access tokens additionally declare typ: "at+jwt" (RFC 9068), so resource servers can reject non-access tokens by header alone. Refresh and ID tokens carry no typ. This page lists every claim AuthPI sets in each token type.

Access token claims

ClaimTypeDescription
issstringIssuer URL: https://idp.authpi.com/{issuer_id} (issuer IDs use the i_ prefix).
substringSubject. The user ID (usr_...) for user tokens; the client ID (c_...) or agent ID (agt_...) for client_credentials tokens.
audstringAudience. See aud precedence below.
expnumberExpiration time (Unix seconds): iat + the configured TTL.
iatnumberIssued-at time (Unix seconds).
auth_timenumberSet to the token’s own iat at issuance—including access tokens minted on refresh—so it reflects issuance time, not the original login. Omitted from refresh tokens. For the user’s actual authentication time, use the ID token’s auth_time.
jtistringUnique token ID (18-character random string). Always present. Used for refresh-token rotation and replay detection.
sidstringSession ID (s_...). Present on user tokens; absent on client_credentials tokens, which have no session.
client_idstringThe client that requested the token (RFC 9068).
datobjectData attestation: { "type": "identity" | "agent" | "key" | "token" }. identity for user tokens, agent for agent client_credentials tokens. Use it as a typed guard alongside the sub prefix.
scopestringSpace-separated granted scopes, preserved across refreshes. A refresh request may narrow (never widen) the scope via the optional scope parameter (RFC 6749 §6); the rotated refresh token always retains the full grant. Defaults to "openid" when no scopes were set; omitted on agent tokens that carry no scopes.
organizationsarrayThe user’s active org memberships after selected-org and client restriction filtering, as [{ "id": "org_...", "title": null, "scopes": [...], "joined_at": 1767312000 }]. Present (possibly []) on user access tokens; omitted from refresh and client_credentials tokens.
org_idstringPresent only when the authorization request selected one organization with org=org_.... Marks that the token set is restricted to that organization.
noncestringEcho of the client-supplied nonce, when one was sent.

ID token claims

ID tokens are issued when the openid scope is granted, alongside the access token (and again on refresh). Standard claims are gated on the granted scopes per OIDC Core §5.4: the email claims require the email scope, and the profile claims require the profile scope. Within a granted scope, a claim is present only when the corresponding profile data exists.

ClaimTypeDescription
issstringIssuer URL, identical to the access token’s iss.
substringThe user ID (usr_...).
audstringAlways the client_id (OIDC Core §2). Never a custom resource audience.
expnumberExpiration (Unix seconds). The ID token lifetime is the client’s default_id_token_age, falling back to its access-token age.
iatnumberIssued-at time (Unix seconds).
auth_timenumberTime of the user’s authentication. Set by the authorization-code flow; absent on ID tokens issued via refresh.
datobjectAlways { "type": "identity" }.
emailstringThe user’s primary email address. Requires the email scope.
email_verifiedbooleanVerification status of the primary email. Requires the email scope.
namestringDisplay name. Requires the profile scope.
given_namestringFirst name. Requires the profile scope.
family_namestringLast name. Requires the profile scope.
picturestringPhoto URL. Requires the profile scope.
countrystringCountry from the user’s address, when set. Requires the profile scope.
organizationsarraySame array shape as access tokens and /userinfo: [{ "id": "org_...", "title": null, "scopes": [...], "joined_at": 1767312000 }]. AuthPI extension—not scope-gated.
org_idstringPresent only when the authorization request selected one organization with org=org_....
updated_atnumberLast profile update (Unix seconds). Requires the profile scope.
noncestringEcho of the client-supplied nonce, when one was sent.
at_hashstringAccess-token hash, binding this ID token to its access token.
acrstringAuthentication context class reference (e.g. urn:authpi:acr:password). Added by the authorization-code flow.
amrarrayAuthentication method references—the method(s) used to sign in (e.g. ["password"], ["sso"]). Added by the authorization-code flow.

Organization claim filtering

Access tokens, ID tokens, and /userinfo responses use the same organizations array shape. Refresh tokens do not carry full organization memberships.

The array is computed at issuance from the user’s current active memberships, then filtered by:

  • The optional org=org_... authorization parameter, which narrows the token set to one organization.
  • The client’s settings.restrictions.organizations policy: all, none, or allowlist.
  • Active status at both levels: the membership and the organization. Memberships of suspended organizations are excluded from newly issued tokens.

See Multi-org tokens for the propagation contract.

Refresh token claims

Refresh tokens are JWTs produced by the same generator as access tokens, with these differences:

  • aud is always the client_id, regardless of any requested resource audience.
  • auth_time is omitted.
  • organizations is omitted. Refresh re-reads current memberships instead of copying old organization claims.
  • org_id is present only when the original authorization selected an organization; refresh keeps that same restriction.
  • scope always carries the full original grant — it is the grant of record. Narrowing a refresh request (RFC 6749 §6) narrows the issued access token only; the rotated refresh token keeps the complete grant, so a later refresh can return to the full scope set.
  • The default lifetime is 7 days (default_refresh_token_age).
  • Rotation: every refresh_token grant returns a new refresh token and invalidates the old one’s jti. Reusing a rotated refresh token marks the session compromised and revokes all of its tokens.

Treat refresh tokens as opaque credentials—their claims are an implementation detail of the rotation machinery, not an API for your application.

Audience (aud) precedence

The access-token aud is resolved in this order: explicit requested audience → client_id → issuer ID. The issuer-ID fallback only applies when no client is involved; in practice all flows supply a client_id.

Tokenaud value
Access token (authorization code)The audience parameter from /authorize (RFC 8707), if sent; otherwise the client_id. A custom audience must equal the client_id or appear in the client’s allowed_audiences, else the request fails with invalid_request.
Access token (refresh grant)The original session audience is preserved across refreshes; falls back to the client_id.
Access token (client_credentials)The resource parameter, if sent; otherwise the client_id (c_... or agt_...).
Refresh tokenAlways the client_id.
ID tokenAlways the client_id.

Resource servers should verify aud against the audience they expect (your API’s identifier if you use RFC 8707, otherwise the client ID).

Token lifetimes

TTLs are configured per client under settings.openid (values in seconds):

SettingApplies toDefault
default_access_token_ageAccess tokens (authorization code, refresh, and M2M client_credentials)1800 (30 minutes)
default_refresh_token_ageRefresh tokens604800 (7 days)
default_id_token_ageID tokensSame as the access-token age when unset
Agent client_credentials tokens (agt_...)Fixed 300 (5 minutes), not configurable—agents re-mint per cycle so scope changes propagate within one TTL window

All three configurable lifetimes are capped at 21 days (1814400 seconds); the API rejects any higher value. This ceiling exists because signing keys are retired ~45 days after creation, so no token may outlive its key’s publication—see JWKS & key rotation.

Example: decoded access token

A user in two organizations, with an RFC 8707 audience requested at /authorize:

{
  "iss": "https://idp.authpi.com/i_8fk2mqzr4tw1ab",
  "sub": "usr_0bk7qmxw2e9rj4t8vhzn3a5cd",
  "aud": "https://api.example.com",
  "exp": 1781262000,
  "iat": 1781260200,
  "auth_time": 1781260200,
  "jti": "Qw7Rt2Xk9Lm4Np6Zs1",
  "sid": "s_7d3f9a1c5e8b2f4d6a0c9e7b3f5d8a1c",
  "client_id": "c_0fj9qkw2tx8mre4hbz7n3vc5a",
  "dat": { "type": "identity" },
  "scope": "openid profile email",
  "organizations": [
    {
      "id": "org_0gw3hcq8r2kfn7xj9tzm4be5a",
      "title": "Founder",
      "scopes": ["owner", "billing:write"],
      "joined_at": 1767312000
    },
    {
      "id": "org_0hk2tqvw8m3rfe9pjx5zcn4ba",
      "title": null,
      "scopes": ["member", "projects:read"],
      "joined_at": 1773100800
    }
  ]
}

Example: decoded ID token

The same user and session—note the client_id audience and the same organizations array shape:

{
  "iss": "https://idp.authpi.com/i_8fk2mqzr4tw1ab",
  "sub": "usr_0bk7qmxw2e9rj4t8vhzn3a5cd",
  "aud": "c_0fj9qkw2tx8mre4hbz7n3vc5a",
  "exp": 1781262000,
  "iat": 1781260200,
  "auth_time": 1781260185,
  "dat": { "type": "identity" },
  "email": "jane@acme.example",
  "email_verified": true,
  "name": "Jane Doe",
  "given_name": "Jane",
  "family_name": "Doe",
  "picture": "https://cdn.acme.example/avatars/jane.png",
  "country": "FR",
  "updated_at": 1780531200,
  "nonce": "n-0S6_WzA2Mj",
  "at_hash": "qYrCZTjHTTjjLMZS6Wf5xg",
  "acr": "urn:authpi:acr:password",
  "amr": ["password"],
  "organizations": [
    {
      "id": "org_0gw3hcq8r2kfn7xj9tzm4be5a",
      "title": "Founder",
      "scopes": ["owner", "billing:write"],
      "joined_at": 1767312000
    },
    {
      "id": "org_0hk2tqvw8m3rfe9pjx5zcn4ba",
      "title": null,
      "scopes": ["member", "projects:read"],
      "joined_at": 1773100800
    }
  ]
}

Next steps