Quickstart
Three calls, start to finish. You need a credential — { apiKey, apiSecret } —
from self-serve signup or your admin console, and
your tenant slug.
1. Mint a bearer token
Section titled “1. Mint a bearer token”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", "expiresAt": <epoch ms>, … }The token is a 24-hour JWT. Send it as Authorization: Bearer <token> on every
call below. Revoke early with POST /api/auth/api-token/revoke ({ token }).
The mint endpoint is one of the few that resolves without a tenant. Every
/api/v1/* call below must also say which tenant it is for: send
X-Tenant-Slug: <your-slug>, or call from an origin registered on your tenant.
Miss it and you get 404 {"error":"unknown_tenant_origin"} — a 404 here means
“no tenant”, not “no such endpoint”. See Tenancy.
2. Read account state
Section titled “2. Read account state”curl -s https://api.troncharts.xyz/api/v1/accounts/$ACCOUNT_ID/state \ -H "authorization: Bearer $TOKEN" \ -H "x-tenant-slug: $TC_TENANT_SLUG"# → { accountId, accountNumber,# perVenue: [ { venue, exchange, equityUsd, balanceUsd, marginUsedUsd,# marginAvailableUsd, unrealizedPnlUsd, … } ] }There are no top-level balance or equity fields: every figure is per venue,
USD-denominated, and returned as a decimal string. The account total is the sum
across perVenue.
Everything readable about an account — positions, orders, trades, fills,
funding — hangs off the same /api/v1/accounts/{accountId}/* prefix. See
Accounts & positions.
3. Place an order intent
Section titled “3. Place an order intent”Requires a fullTrading credential tier.
curl -s https://api.troncharts.xyz/api/v1/oms/intents \ -H "authorization: Bearer $TOKEN" \ -H "x-tenant-slug: $TC_TENANT_SLUG" \ -H 'content-type: application/json' \ -H "idempotency-key: $(uuidgen)" \ -d '{ "venue": "hyperliquid", "symbol": "BTC.HL", "side": "buy", "type": "limit", "qty": "0.01", "price": "60000" }'# → { "intentId": "…", … }One endpoint covers market / limit / stop / stop_limit /
take_profit across every venue. An order is an intent the platform
composes, signs, and dispatches; the venue-side order id and the fills arrive
over /ws/risk rather than by polling.
accountId is optional — omit it and the credential’s active account is used.
Always send an idempotency-key on OMS writes — /api/v1/oms/* is the surface
that honours it; see Conventions.
The same three steps in TypeScript
Section titled “The same three steps in TypeScript”The client is constructed with a token — there is no anonymous client, so
mint the token first with a plain fetch. Pass tenantSlug and the SDK sends
X-Tenant-Slug on every request for you.
import { TronCharts } from '@tronchartsxyz/api-client'
const res = await fetch('https://api.troncharts.xyz/api/auth/api-token', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ apiKey, apiSecret }),})const { token } = (await res.json()) as { token: string }
const sdk = new TronCharts({ baseUrl: 'https://api.troncharts.xyz', token, tenantSlug: 'your-slug',})
const state = await sdk.accounts.state(accountId)const { intentId } = await sdk.oms.composeIntent({ venue: 'hyperliquid', symbol: 'BTC.HL', side: 'buy', type: 'limit', qty: '0.01', price: '60000',})- Base URLs & discovery — never hardcode a venue list.
- Scopes & tiers — what your credential is allowed to do.
- Connecting — stream state instead of polling.