# Credentials & tokens

Every integration call is made with a **credential** — `{ apiKey, apiSecret }`
— that you exchange for a short-lived bearer token. The credential also carries
the [scopes and tier](https://docs.troncharts.xyz/docs/auth/scopes/) that decide what you can do.

:::note[First decide which of the two you need]
A credential is minted as either a **trader** (acts on its own account, can
place orders) or an **operator** (administers your tenant, never places orders).
It is one or the other, permanently, and each reaches a different half of the
API. See [Trader vs operator](https://docs.troncharts.xyz/docs/auth/audiences/) before minting — an actor
that both administers and trades needs one of each.
:::

## 1. Get a credential

Two ways:

- **Self-serve signup** — `POST /api/public/tenants/signup` with your email and
  a slug, then `POST /api/public/tenants/verify` with the emailed token. Verify
  provisions your tenant, an admin invite, and (where self-serve issuance is
  enabled) an operator credential carrying `firm:operate` + `prop:manage`.
- **Admin console** — an existing tenant issues a credential from the API
  Clients screen and picks its scopes.

The secret is shown **once**. Store it like a password.

## 2. Exchange it for a bearer token

```bash
curl -s https://api.troncharts.xyz/api/auth/api-token \
  -H 'content-type: application/json' \
  -d '{"apiKey":"…","apiSecret":"…"}'
# → { "token": "eyJ…", "tier": "…", "expiresAt": <epoch ms>, … }   # 24h JWT
```

Send `Authorization: Bearer <token>` on every `/api/v1/*` call, along with
`X-Tenant-Slug` (or call from an origin registered on your tenant) — without a
tenant the request 404s before your token is read. See
[Tenancy](https://docs.troncharts.xyz/docs/auth/tenancy/). The mint call above is one of the few paths
exempt from that gate.

Revoke a single token before it expires with `POST /api/auth/api-token/revoke`,
passing `{ token }`.

The SDK client is constructed with a token, so mint it first and then build the
client:

```ts
const sdk = new TronCharts({
  baseUrl: 'https://api.troncharts.xyz',
  token,
  tenantSlug: 'your-slug',
})
```

## Cookie sessions vs bearer sessions

Two authentication paths reach the same API:

| Path | Who uses it | How |
| --- | --- | --- |
| **Cookie session** | Browser front ends | SIWE or social sign-in yields a `tron_session` cookie; same-origin requests authenticate automatically. |
| **Bearer JWT** | Integrators, algos, servers | Minted from a credential as above. |

Use bearer for anything server-side. A cookie session is scoped to a browser
and a single origin; it is not a machine credential.

## WebSocket handshake tokens

The WebSocket channels do **not** accept the bearer token in the `Authenticate`
frame. Call `POST /api/auth/bootstrap` — authenticating either with
`{ apiKey, apiSecret }` in the body or with a bearer token and no body — and it
returns a short-lived, single-use handshake token per channel. That handshake
token is what the socket wants. See [Connecting](https://docs.troncharts.xyz/docs/realtime/connecting/).

Unlike the bearer mint, `/api/auth/bootstrap` is tenant-gated, so send
`X-Tenant-Slug` on it too.

## Rotation

Rotate by issuing a second credential, deploying it, then revoking the first.

Revoking a credential takes effect immediately for minting new tokens, for order
placement, modification and cancellation by tokens already outstanding, and for
the `firm:operate` / `prop:manage` / `tenant:config` scope gates — though those
three are cached briefly, so allow a few minutes.

:::caution[Revoking a credential is not a kill switch for a leaked token]
It does not stop **reads**. An access token already minted from that credential
keeps working on the read surface — accounts, positions, orders, market data —
until its own 24-hour expiry, because the read path checks the token's
signature, expiry, and per-token revocation list, not the credential row behind
it.

So if a *token* leaked, revoke the token: `POST /api/auth/api-token/revoke` with
`{ token }`, once per outstanding token. Revoking the credential alone leaves
the leaked token reading for up to a day.
:::