Scopes
Scopes control which endpoints an access token can reach. When you issue a token, select only the scopes the consumer actually needs. A request made with a token that lacks the scope an endpoint requires is rejected with 403 Forbidden.
There are nine categorical scopes: four personal-grant scopes any user may issue on their own tokens, and five admin scopes that only an admin user may issue. The model is deliberately coarse. Each scope names a category of data or action rather than a single resource, so there is one short list to reason about rather than dozens of fine-grained literals.
If you hold a token issued before this model landed, see the categorical scopes migration guide for how your old scopes were translated.
Personal-grant scopes
Any user may issue these on their own tokens.
| Scope | Grants | Does not grant |
|---|---|---|
trading:read | Read access to trades, positions, orders, executions, signals, strategies, performance, pending imports, and the dashboard summary | Any write. No order placement, no signal creation, no trade edits. No other user's data. |
accounts:read | Read access to accounts, brokers, balances, reconciliation reads, sync status, and licence status | Connecting or disconnecting brokers, changing broker credentials, triggering a sync, or setting the primary account. No other user's data. |
activity:read | Read access to your activity feed, notifications, and notification preferences, including your own audit log | Changing notification preferences. No other user's activity or audit log. |
signals:write | Create, update, and cancel signals against any account you have access to, plus manual and batch signal processing | Direct order placement (POST /orders), trade edits, or any read scope. Pair it with trading:read if the consumer also needs to read back what it submitted. |
signals:write is the supported path for automation. UTM translates a signal into broker orders, applies your strategy and exit rules, and deduplicates intent. Direct order placement is never delegated to a token (see Never-delegate below).
Admin scopes
Only an admin user may issue these. A non-admin who names an admin scope at issuance receives a 400.
| Scope | Grants | Does not grant |
|---|---|---|
admin:read | System-wide, non-user-bound reads: queue stats, monitoring metrics, sessions, health invariants, aggregate system state | Any per-user data. No identity fields. No writes. |
admin:read:user | Per-user reads with userIds as opaque identifiers: another user's trades, positions, orders, signals, accounts, broker state, audit-log entries, and system-log entries, with no identity join | The userId-to-identity join. Responses carry the userId UUID, never email, name, phone, or billing. No writes. |
admin:read:identity | The userId-to-identity join: the user list and user detail, where responses include email, name, phone, billing, and free-text user-content fields | Any write. This is the only scope that can resolve a userId to a real-world identity. |
admin:write | Non-destructive single-record admin fixes: settings updates, config changes, user role and status edits, and queue control (retry, pause, trigger) | Mass or irreversible operations. No permanent deletes, no bulk clears. |
admin:destructive | Mass or irreversible operations: permanent user delete, clear-all failed jobs, and per-queue failed-job clears | Nothing beyond the destructive operations themselves. It is not a superset of the read scopes; pair it with the reads a workflow needs. |
admin:read:user and admin:read:identity are split on purpose. Most cross-user diagnostics only need to correlate events by userId, which is pseudonymous. Reserve admin:read:identity for the rare case that genuinely needs to resolve a userId to a person, and keep it off tokens that do not.
Never-delegate operations
Some operations can only be performed by the user in the web app with a JWT session. No access token, at any scope, can perform them. These are the operations where delegating to a long-lived token would either let the token undermine the identity that issued it, escalate its own privilege, or touch financial, regulatory, or invariant-sensitive state.
| Category | Operations | Why it is never delegated |
|---|---|---|
| Identity | Password, MFA, email change, account deletion | A token must not be able to revoke or rewrite the identity that issued it. |
| Tokens | Issuing and managing access tokens | Prevents a token from minting more privilege for itself. |
| Subscription | Payment, plan changes, cancellation | Financial and regulatory. |
| Broker connections | OAuth, credential updates, connect, disconnect | Could silently re-route where your trades execute. |
| Personal data | Export and erasure | Privacy obligations sit with the authenticated person. |
| Trade hygiene | Close, edit, or delete trades, and reconciliation writes | Invariant-sensitive. The signal lifecycle is the supported automation path. |
| Account configuration | Sync trigger, primary set, UTM start date, broker config | Operator-level account setup, not delegated. |
| Strategy curation | Create, edit, or delete strategy templates | Curated by hand in the app. |
| Direct order placement | POST /orders, DELETE /orders/:id | Signals are the supported broker-translation layer for automation. |
Why there is no :live dimension
Scopes do not distinguish paper from live trading, and there is no :live suffix on any scope. Trading mode is a client-side preference, not a security boundary. A token that can submit a signal can submit it against any account the user owns, paper or live alike, exactly as the user can in the app.
Two reasons drove this:
- The account already carries its own type. Whether an order lands against a paper or a live account is decided by the
accountTypeon the account row the signal targets, not by a claim on the token. Gating on a token-level:liveflag would duplicate that decision in a second place that could disagree with the first. - A
:livedimension roughly doubles the scope surface for a distinction the platform does not actually enforce at the token layer. The earlier session-mode JWT claim that tried to enforce it was removed because it added a cross-check that the account type already settled.
If you want a token restricted to paper trading, restrict it at the account level by only granting the consumer access to paper accounts, not by reaching for a scope that does not exist.
Requesting a token and the scope picker
Tokens are issued from Settings -> Access tokens. See Access tokens for the full create, use, and revoke flow.
The scope picker on the create form renders exactly the scopes you are allowed to issue:
- Every user sees the four personal-grant checkboxes.
- Admin-role users additionally see the five admin checkboxes.
A non-admin user has no way to select an admin scope, and the API rejects one even if it is sent directly. Select the minimum the integration needs; you can always issue a second, narrower token rather than over-scoping one.
Common scope combinations
| Use case | Scopes |
|---|---|
| TradingView or webhook signal submission | signals:write |
| Read-only portfolio monitor | trading:read, accounts:read |
| Trading journal sync | trading:read, accounts:read |
| Equity-curve and balance dashboards | trading:read |
| Strategy-aware automation | trading:read, signals:write |
| Full personal read access | trading:read, accounts:read, activity:read |
Scope enforcement
A request with a token that lacks the required scope receives 403 Forbidden:
{
"error": "Insufficient scope",
"code": "INSUFFICIENT_SCOPE",
"required": ["trading:read"],
"granted": ["accounts:read"]
}
Each endpoint in the API reference documents the scope it requires.
Revoking a token
Revoke a token from Settings -> Access tokens by clicking the trash icon next to its row and confirming. The token is invalidated immediately and the revocation cannot be undone; issue a fresh token to replace it. Full details are on the Access tokens page.
One credential type: access tokens
API keys and connected-app tokens have collapsed into a single credential type: access tokens. They were always functionally identical (named, scoped bearer token, revocable, hashed at rest). The settings page, the storage table, and the scope set are all unified, so there is now one place to issue tokens and one set of scopes to reason about.
Tokens issued before the unification keep working unchanged.