> Markdown version of https://authpi.com/docs/reference/token-claims/ — fetch the complete AuthPI docs index at https://authpi.com/llms.txt to discover all available pages.

# Token Claims Reference

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

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

| Claim | Type | Description |
|-------|------|-------------|
| `iss` | string | Issuer URL: `https://idp.authpi.com/{issuer_id}` (issuer IDs use the `i_` prefix). |
| `sub` | string | Subject. The user ID (`usr_...`) for user tokens; the client ID (`c_...`) or agent ID (`agt_...`) for `client_credentials` tokens. |
| `aud` | string | Audience. See [`aud` precedence](#audience-aud-precedence) below. |
| `exp` | number | Expiration time (Unix seconds): `iat` + the configured TTL. |
| `iat` | number | Issued-at time (Unix seconds). |
| `auth_time` | number | Set 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`. |
| `jti` | string | Unique token ID (18-character random string). Always present. Used for refresh-token rotation and replay detection. |
| `sid` | string | Session ID (`s_...`). Present on user tokens; absent on `client_credentials` tokens, which have no session. |
| `client_id` | string | The client that requested the token (RFC 9068). |
| `dat` | object | Data 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. |
| `scope` | string | Space-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. |
| `organizations` | array | The 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_id` | string | Present only when the authorization request selected one organization with `org=org_...`. Marks that the token set is restricted to that organization. |
| `nonce` | string | Echo 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.

| Claim | Type | Description |
|-------|------|-------------|
| `iss` | string | Issuer URL, identical to the access token's `iss`. |
| `sub` | string | The user ID (`usr_...`). |
| `aud` | string | **Always the `client_id`** (OIDC Core §2). Never a custom resource audience. |
| `exp` | number | Expiration (Unix seconds). The ID token lifetime is the client's `default_id_token_age`, falling back to its access-token age. |
| `iat` | number | Issued-at time (Unix seconds). |
| `auth_time` | number | Time of the user's authentication. Set by the authorization-code flow; absent on ID tokens issued via refresh. |
| `dat` | object | Always `{ "type": "identity" }`. |
| `email` | string | The user's primary email address. Requires the `email` scope. |
| `email_verified` | boolean | Verification status of the primary email. Requires the `email` scope. |
| `name` | string | Display name. Requires the `profile` scope. |
| `given_name` | string | First name. Requires the `profile` scope. |
| `family_name` | string | Last name. Requires the `profile` scope. |
| `picture` | string | Photo URL. Requires the `profile` scope. |
| `country` | string | Country from the user's address, when set. Requires the `profile` scope. |
| `organizations` | array | Same array shape as access tokens and `/userinfo`: `[{ "id": "org_...", "title": null, "scopes": [...], "joined_at": 1767312000 }]`. AuthPI extension—not scope-gated. |
| `org_id` | string | Present only when the authorization request selected one organization with `org=org_...`. |
| `updated_at` | number | Last profile update (Unix seconds). Requires the `profile` scope. |
| `nonce` | string | Echo of the client-supplied nonce, when one was sent. |
| `at_hash` | string | Access-token hash, binding this ID token to its access token. |
| `acr` | string | Authentication context class reference (e.g. `urn:authpi:acr:password`). Added by the authorization-code flow. |
| `amr` | array | Authentication 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](/docs/concepts/organizations/#status-management) are excluded from newly issued tokens.

See [Multi-org tokens](/docs/concepts/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`.

| Token | `aud` 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 token | Always the `client_id`. |
| ID token | Always 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):

| Setting | Applies to | Default |
|---------|-----------|---------|
| `default_access_token_age` | Access tokens (authorization code, refresh, and M2M `client_credentials`) | `1800` (30 minutes) |
| `default_refresh_token_age` | Refresh tokens | `604800` (7 days) |
| `default_id_token_age` | ID tokens | Same 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](/docs/reference/jwks-key-rotation).

## Example: decoded access token

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

```json
{
  "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:

```json
{
  "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

- [Multi-org tokens](/docs/concepts/multi-org-tokens/) — default multi-org tokens, selected-org tokens, and the propagation contract
- [OIDC & OAuth 2.0 compliance](/docs/reference/oidc/) — grants, PKCE, introspection, revocation, and discovery
- [Clients](/docs/concepts/clients/) — configuring TTLs, allowed audiences, and signature algorithms
- [TypeScript backend quickstart](/docs/quickstarts/typescript-backend/) — verify these tokens in your API