# Mint a token and make your first call

Every REST call is authenticated with a short-lived bearer token you mint yourself
from a long-lived credential. You end up with a token, the two headers every later
call needs, and a response that proves both of them resolved.

1. ### Exchange the credential for a token

   `POST /api/auth/api-token` takes `{ apiKey, apiSecret }` and returns a 24-hour
   HS256 JWT in `token`. The credential pair itself is issued out of band — by an
   operator in the admin console, or by self-serve tenant signup where that is
   enabled. See [Credentials & tokens](https://docs.troncharts.xyz/docs/auth/credentials/).

   ```bash
   curl -s https://api.troncharts.xyz/api/auth/api-token \
     -H 'content-type: application/json' \
     -d '{"apiKey":"'"$TC_API_KEY"'","apiSecret":"'"$TC_API_SECRET"'"}'
   # → { "token": "eyJ…", "tier": "fullTrading", "accountId": "…",
   #     "expiresAt": 1754300000000, "enabledTools": null, … }
   ```
   ```ts
   import { TronCharts } from '@tronchartsxyz/api-client'

   // The mint path authenticates from the body, so the minting client
   // needs no token of its own.
   const minter = new TronCharts({ baseUrl: 'https://api.troncharts.xyz', token: '' })
   const { token } = await minter.auth.apiToken({
     apiKey: process.env.TC_API_KEY!,
     apiSecret: process.env.TC_API_SECRET!,
   })
   ```
   `tier` is snapshotted at mint: a tier change on the credential needs a fresh
   token to take effect.

2. ### Send the token and the tenant together

   `GET /api/v1/_meta/test-connection` is the one round-trip that confirms the
   bearer, the tenant and the tier all resolved. It returns
   `{ ok, accountId, apiClientId, tier, serverTime }`.

   :::caution[Both headers, on every `/api/v1` call]
   `/api/v1/*` resolves a tenant *before* it reads your token, and an origin no
   tenant claims is refused with `404 unknown_tenant_origin`. Miss `x-tenant-slug`
   and you get a 404 that reads like a wrong URL — see [Tenancy](https://docs.troncharts.xyz/docs/auth/tenancy/).
   :::

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/_meta/test-connection \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG"
   # → { "ok": true, "accountId": "…", "apiClientId": "…",
   #     "tier": "fullTrading", "serverTime": "2026-08-04T12:00:00.000Z" }
   ```
   ```ts
   const sdk = new TronCharts({
     baseUrl: 'https://api.troncharts.xyz',
     token,
     tenantSlug: process.env.TC_TENANT_SLUG!,  // sent as x-tenant-slug on every request
   })

   // No typed resource wraps /_meta; sdk.client is the escape hatch.
   const who = await sdk.client.request<{ ok: boolean; accountId: string; tier: string }>(
     '/api/v1/_meta/test-connection',
   )
   ```
3. ### Read something back

   `GET /api/v1/accounts` lists every account the credential can reach and gives you
   the `accountId` that every `/api/v1/accounts/{id}/*` read needs.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/accounts \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG"
   # → { rootAccountId, activeAccountId,
   #     accounts: [ { accountId, accountNumber, kind, capitalModel,
   #                   displayName, baseCurrency, wallets: [...] } ] }
   ```
   ```ts
   const { accounts } = await sdk.accounts.list()
   // The route sends `accountId` on each row; the SDK's `id` field is not
   // on the wire in 0.3.0.
   const accountId = accounts[0]!.accountId as string
   ```
   Revoke a token before its 24 hours are up with
   `POST /api/auth/api-token/revoke` and `{ token }`.

**Next:** [Create a paper account](https://docs.troncharts.xyz/docs/recipes/create-a-paper-account/) · [Read balance, positions and open orders](https://docs.troncharts.xyz/docs/recipes/read-account-state/) · [Scopes & tiers](https://docs.troncharts.xyz/docs/auth/scopes/)