Skip to content

Trader vs operator credentials

Two very different integrations use this API, and they need different credentials:

  • a trader — a person or bot acting on their own account: placing orders, reading their positions, streaming their fills;
  • an operator (partner / tenant) — a business acting on its tenant: creating traders, prop firms, challenge templates, risk profiles and trading groups, approving payouts.

A credential is minted for exactly one of these. The audience is fixed at mint and never changes, and one credential can never be both.

Trader credential Operator credential
Acts on Its own account Your tenant
Minted from The account, in the browser — Settings → API Clients The admin console — API Clients
Carries A tier (readonly / fullTrading), a risk envelope, venue + symbol allowlists, and an agent wallet Scopes: prop:manage, firm:operate, tenant:config, accounts:intervene
Can place orders Yes No — never
Can administer a tenant No — scopes are rejected at mint Yes, and only its own tenant

Not a missing feature, and not something to work around. Authority over an account comes from that account, never from a credential: a trader credential can place orders because the account’s own wallet signed an agent authorization for it, in the browser, where the wallet is. An operator has no access to that wallet and cannot forge that signature — the constraint is cryptographic.

So an operator credential holds no agent wallet by design. Presenting one on the trading surface returns 403 wrong_credential_audience.

Use the client that matches your credential. Calling the other surface then fails at compile time instead of at runtime.

import { TronChartsTrader, TronChartsOperator } from '@tronchartsxyz/api-client'
// Acting on your own account
const trader = new TronChartsTrader({ baseUrl, token: traderToken, tenantSlug })
await trader.oms.place({ /* … */ })
await trader.accounts.state(accountId)
// Administering your tenant
const ops = new TronChartsOperator({ baseUrl, token: operatorToken, tenantSlug })
const { user } = await ops.users.create({ externalId: 'crm-4471' })
await ops.propTemplates.create({ /* … */ })
Client Resources
TronChartsTrader oms · accounts · propAccounts · market · reports · sor · venues · kyc · referrals · analytics · indicators · backtests · copyTrading
TronChartsOperator users · firms · propTemplates · riskProfiles · tradingGroups · tenant · sandbox · propAccounts (read-only oversight) · reports · analytics

TronCharts still exposes both surfaces on one object. It works, and it tells you nothing about which half your token can reach — prefer the scoped clients.

{
"error": "wrong_credential_audience",
"detail": "this endpoint is for operator credentials, but this is a trader credential. …",
"expected": "operator",
"actual": "trader"
}

HTTP 403. The fix is never to change the credential you have — audiences are immutable — but to mint the other one and keep both.

You may still meet the older, less direct errors underneath this gate:

Error What it actually means
wrong_credential_audience Wrong kind of credential. Mint the other one.
credential_missing_agent_wallet A trader credential minted without an agent wallet. Re-mint it from the account it should trade.
tier_insufficient A trader credential whose tier is too low — readonly cannot open positions.
forbidden on an operator route Right audience, missing that specific scope. An operator grants scopes per credential.

A cookie session (your own logged-in user in the FE) carries no credential and no audience, so this gate does not apply to it. Each route handles it on its own terms: the operator surface refuses it outright — tenant administration is an explicit, audited credential capability — while the trading surface accepts it as the trader it represents.

Next: Credentials & tokens · Scopes & tiers · How an account is configured