> ## Documentation Index
> Fetch the complete documentation index at: https://docs.endstate.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Session tokens

> Short-lived proof that a specific chip was just tapped. Scoped to one chip, unit, and organization — safe to pass to a client without exposing your API key.

A session token (`end_sess_...`) is issued automatically when a tap is verified. It records that a specific chip was physically tapped moments ago. It is not proof of ownership — it is proof of a recent tap.

## What a session token represents

When your server verifies a tap with `POST /v1/chips/{chip_id}`, the API:

1. Validates the one-time credential (`e` value).
2. Records the tap against the chip's scan count.
3. Returns a `session_token` object bound to that chip, the unit it is paired to, and your organization.

The token carries no identity claim beyond that tap event. If you need to authorize a unit-scoped action on behalf of a user (for example, confirming they are holding the physical product), pass the session token to that action instead of your API key.

<Warning>
  Never expose your API key (`end_sk_...`) to a browser or mobile client.
  Session tokens are designed to be the credential you hand to a client after
  server-side verification.
</Warning>

## How a session token is issued

Call `POST /v1/chips/{chip_id}` with the `e` value from the tap. The response includes:

| Field                      | Type   | Description                                                             |
| -------------------------- | ------ | ----------------------------------------------------------------------- |
| `session_token.token`      | string | The opaque `end_sess_...` credential. Returned once; never recoverable. |
| `session_token.expires_at` | string | ISO-8601 timestamp when the token expires.                              |

**Default lifetime:** 600 seconds. To request a different lifetime, pass `ttl` (integer, 60–3600) in the verify request body. The response `expires_at` reflects the actual expiry.

The token is returned exactly once in the verify response. If you lose it, you must re-verify (perform another tap) to get a new token.

## Security properties

* **Opaque.** The token carries no decodable payload. Its meaning is defined entirely by the introspection response.
* **Single-issue.** Returned once in the verify response. Not logged or retrievable after that.
* **Short-lived.** Expired tokens are rejected immediately. The default 600-second window is enough for a user interaction; request a shorter `ttl` for higher-sensitivity flows.
* **Client-safe.** Unlike your API key, a session token is scoped to one chip and one unit. Passing it to a browser client exposes nothing beyond the current tap event.

## Introspecting a session token

Use `GET /v1/session-tokens/current` to confirm what a session token is bound to. Authenticate the request with the session token itself — not your API key.

```bash theme={null}
curl https://api2.endstate.io/v1/session-tokens/current \
  -H "Authorization: Bearer end_sess_..."
```

**Trimmed response:**

```json theme={null}
{
  "chip_id": "ABCDEF0123",
  "unit_id": "8e1a7f50-90ab-4cde-f012-3456789abcde",
  "organization_id": "3b2c1d0e-1234-5678-abcd-ef0123456789",
  "expires_at": "2026-05-14T10:40:00.000Z"
}
```

<ResponseField name="chip_id" type="string">
  The 10-character hex identifier of the chip that was tapped.
</ResponseField>

<ResponseField name="unit_id" type="string">
  The UUID of the unit the chip is paired to.
</ResponseField>

<ResponseField name="organization_id" type="string">
  The UUID of your organization. Confirms the token belongs to your account.
</ResponseField>

<ResponseField name="expires_at" type="string">
  ISO-8601 timestamp after which the token is no longer valid.
</ResponseField>

A client can call this endpoint to confirm tap scope — which chip, which unit, which organization — without holding your API key.

<Note>
  Calling the API **directly from a browser** requires your web origin to be on
  your organization's CORS allowlist — requests from unlisted origins are
  blocked by the browser before they reach the endpoint. Ask Endstate to
  allowlist your origins during onboarding. Server-side calls are unaffected.
</Note>

## Use cases

Session tokens are most useful when you need to prove that a user physically tapped a product before allowing a unit-scoped action:

* **Gating digital content or experiences.** After verification, pass the session token to your own backend or a client to unlock content tied to that unit.
* **Logging user interactions.** Your server receives the tap event from the verify response; the session token lets a client confirm the same tap without a second server call.
* **Handoff to a client flow.** Verify server-side, then pass `end_sess_...` to the client. The client can introspect to display unit details without access to your API key.

<Note>
  A chip-tap session token authorizes the unit's ownership actions: **claiming**
  it — handing it to its first owner — via the [Claims API](/concepts/claims),
  and **transferring** it between owners via the [Transfers
  API](/concepts/transfers).
</Note>

## Errors

| Code                               | HTTP | When                                                                      |
| ---------------------------------- | ---- | ------------------------------------------------------------------------- |
| `session_token.invalid_or_expired` | 401  | Token is unknown, has expired, or was revoked.                            |
| `session_token.wrong_chip`         | 403  | Token is valid but bound to a different chip than the one in the request. |

Branch on `error.code`, not on HTTP status or `message`. See [Error conventions](/conventions/errors) for the full error envelope and handling guidance.

## Endpoints

| Method | Path                         | Auth          | Description                            |
| ------ | ---------------------------- | ------------- | -------------------------------------- |
| `POST` | `/v1/chips/{chip_id}`        | API key       | Verify a tap; returns a session token. |
| `GET`  | `/v1/session-tokens/current` | Session token | Introspect the bearer session token.   |

Full request and response schemas are in the [API reference](/api-reference/introduction).

## Next steps

<CardGroup cols={2}>
  <Card title="Chips & verification" icon="scan-line" href="/concepts/chips">
    Learn how chips are paired to units and how taps produce the one-time
    credential used in verification.
  </Card>

  <Card title="Verify a unit" icon="circle-check" href="/guides/verify-a-unit">
    Step-by-step guide to running a complete verification flow and handling the
    session token.
  </Card>
</CardGroup>
