# 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.

## What each can do

| | 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 |

### Why an operator can't trade

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`.

:::note[An actor that both administers and trades needs two credentials]
That is intended, not a workaround: different authority, different blast radius,
different revocation path. Hold both and use the matching client for each — do
not re-mint one as the other, which silently drops everything the first one
carried.
:::

## In the SDK

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

```ts
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.

## When you get it wrong

```json
{
  "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. |

## Browser sessions

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](https://docs.troncharts.xyz/docs/auth/credentials/) ·
[Scopes & tiers](https://docs.troncharts.xyz/docs/auth/scopes/) ·
[How an account is configured](https://docs.troncharts.xyz/docs/launch/account-configuration/)