# How an account is configured

Three objects shape what an account may trade and under what limits. They look
overlapping from the outside, so this page states exactly what each one owns and
which of them you write.

Short version: **you write a trading group and, for prop, a challenge template.**
A risk profile is something the server keeps for you unless you deliberately want
to share one rule set across several templates.

## The provisioning order

```
trading group ──► challenge template ──► trader ──► account
   (required)        (prop only)        (required)
                          ▲
                    risk profile
                     (optional)
```

1. **Trading group** — the bare minimum. A template cannot be created without
   one (`400 trading_group_required`), and every account resolves to one.
2. **Challenge template** — for a prop program. Carries the rules, and points at
   the group from step 1.
3. **Risk profile** — optional, and only when you want to share one rule set
   across templates, or to put limits on a non-prop account. Otherwise the
   server keeps a managed one for you.
4. **Trader** — `POST /api/v1/users`. An account needs an owner, and its `id` is
   the `ownerUserId` the account calls want.
5. **Account** — `POST /api/v1/accounts/create-prop` (prop) or
   `POST /api/v1/accounts` (plain).

### `ownerUserId` is the trader

The name trips people up, so plainly: it is the **person who trades the
account** — a `users.id`. It is not your tenant and not your prop firm.

| Field | Is | Where it comes from |
| --- | --- | --- |
| `ownerUserId` | The **trader** | `POST /api/v1/users` |
| `tenantId` | Your **platform tenant** | Forced from your credential — never sent |
| `propTenantId` | One **prop firm** you operate | `GET /api/v1/firms` |

Naming a trader from another tenant returns `404 owner_user_not_found` rather
than confirming they exist.

```ts
const { user } = await sdk.users.create({ email: 'ana@example.com', externalId: 'crm-4471' })
// user.id → pass as ownerUserId
```

Pass your own `externalId` and the create becomes **idempotent**: a retried
provisioning run returns the same trader (`idempotent: true`) instead of
splitting one person across two identities. Creating a trader does not
authenticate them — they still sign in with a wallet or social auth; this only
establishes the identity accounts hang off.

## What each one owns

| | Owns | Answers |
| --- | --- | --- |
| **Trading group** | Venue and symbol allowlists, fee schedule and rebate share, leverage caps, spread skew and price bands, per-symbol and per-venue overrides | *Which markets, at what price* |
| **Challenge template** | The prop product: account size, entry fee, payout split, duration, tier ladder, next-step chain, reset and activation fees — plus the rule set the run is judged against | *What the trader buys, and how they pass or fail* |
| **Risk profile** | A named, reusable rule set (`trader`, `challenge` or `funded`) | *Which limits apply* |

The layers are a **most-restrictive cascade**, not a hierarchy. A group's venue
allowlist is enforced at compose time; the prop rules are enforced after the
price is resolved. Both run on every order — neither overrides the other, and
each can reject on its own.

## The rules live in one place

A template's rules and its risk profile are the same rules. You write them
**once, on the template**:

```ts
await sdk.propTemplates.create({
  id: 'acme-25k-1step',
  name: 'Acme 25K Evaluation',
  accountSizeUsd: '25000.00',
  feeUsd: '199.00',
  payoutSplitPct: '80',
  tradingGroupId: group.id,
  propTenantId: firm.id,
  rules: [
    { type: 'max_drawdown_usd', value: 1250 },
    { type: 'profit_target_usd', value: 2000 },
  ],
})
```

The server materializes a **managed risk profile** holding those rules and binds
it to the template. You never name it, create it, or bind it. It shows up in
`GET /api/v1/risk-profiles` with `managedByTemplateId` set, and it is read-only
there — `PATCH` and `archive` return `409 managed_by_template`. To change the
rules, patch the template.

That is the whole model for most integrations. The rest of this page is for the
two cases where you touch a profile directly.

### Case 1 — sharing one rule set across templates

Six templates (three sizes × two steps) often share two rule sets. Author the
profile once and reference it, instead of repeating the rules six times and
watching them drift on the next edit:

```ts
const { profile } = await sdk.riskProfiles.create({
  name: 'Acme 1-step', kind: 'challenge',
  rules: [{ type: 'max_drawdown_usd', value: 1250 }],
})

for (const size of ['25000.00', '50000.00', '100000.00']) {
  await sdk.propTemplates.create({ /* … */ accountSizeUsd: size, riskProfileId: profile.id })
}
```

`rules` and `riskProfileId` are **mutually exclusive**. Sending both is
`400 rules_and_profile_conflict` — a template has one source of truth for its
rules, so neither one silently wins. When you send `riskProfileId`, the profile's
rules are copied down into the template so the snapshot the engine freezes onto
each account matches the profile exactly.

While a template reads a shared profile, patching its `rules` returns
`400 rules_locked_by_profile`. Either edit the profile (every template reading it
follows), or send `riskProfileId: null` to move the rules back onto the template,
where they become managed again.

### Case 2 — limits on a non-prop account

A plain live or paper account has no template. To give one limits, author a
`trader` profile and bind it:

```ts
const { profile } = await sdk.riskProfiles.create({
  name: 'Retail default', kind: 'trader', enforcement: 'hard',
  rules: [{ type: 'max_leverage', value: 20 }],
})
await sdk.riskProfiles.bind(profile.id, accountUuid)
```

## What lands on a new account

`POST /api/v1/accounts` accepts `tradingGroupId` and `riskProfileId` directly, so
an account starts on the right scope instead of being created on the tenant
default and re-bound a moment later. Both are optional and both are validated
against your tenant: an unknown group is `400 trading_group_not_found`, and a
**managed** profile is `400 profile_is_managed` — it belongs to its template, so
create the account from that template instead. Omit them and the account
inherits the tenant's default group with no profile bound, exactly as before.

:::note[This endpoint needs a user session]
An api-client bearer has no user of its own, so it gets `403
user_session_required` here. Partner-side account provisioning goes through
`POST /api/v1/accounts/create-prop` with an `ownerUserId`, or
`POST /api/v1/sandbox/paper-account`.
:::

`POST /api/v1/accounts/create-prop` takes a `templateId` and an `ownerUserId`,
and the template carries the rest: its `tradingGroupId` and its risk profile are
both propagated onto the new account, and the rule set is frozen onto the prop
account at creation. So for a prop integration you configure **one** object — the
template — and the group and profile ride along.

A later edit to the template does **not** re-judge accounts already in flight;
they keep the rules they were sold under. New accounts pick up the new rules.

## Which rules are enforced today

| Bound to | Enforced by | Status |
| --- | --- | --- |
| A prop account (from a template) | The prop engine, against the frozen snapshot | **Hard, always on.** A breaching order is rejected with `409 prop_breach_would_occur` before the venue sees it |
| A non-prop account (`trader` profile) | The order-time risk-profile gate | Advisory today — `ENFORCE_RISK_PROFILES` ships off, so a bound `trader` profile annotates rather than blocks |
| Any account, via its trading group | The compose-time group gate | **Hard, always on** for venue and symbol scope |

A managed profile is deliberately **not** evaluated on the risk plane: the prop
engine already enforces the identical rules from the frozen snapshot, so
evaluating them twice would report every rule a second time as an advisory
warning alongside the real verdict.

**Next:** [Create a challenge template](https://docs.troncharts.xyz/docs/recipes/create-a-challenge-template/) ·
[Create a trading group](https://docs.troncharts.xyz/docs/recipes/create-a-trading-group/) ·
[Create a risk profile](https://docs.troncharts.xyz/docs/recipes/create-a-risk-profile/) ·
[Prop account model](https://docs.troncharts.xyz/docs/launch/prop-accounts/)