# Create a risk profile and bind it to an account

A risk profile is a named rule set you write once and attach to many accounts.
When this is done, one account carries `riskProfileId` and the order-time gate
reads that profile on every compose.

:::note[For a single prop program, you don't need this page]
Write the rules on the challenge template instead — the server materializes and
maintains a **managed** profile for you. Author a profile here only to give a
**non-prop** account limits, or to share one rule set across several templates.
See [How an account is configured](https://docs.troncharts.xyz/docs/launch/account-configuration/).

Managed profiles appear in the list with `managedByTemplateId` set and are
read-only: `PATCH` and `archive` return 409 `managed_by_template`, naming the
template to edit instead. Filter on `!managedByTemplateId` to see only the
profiles you author.
:::

1. ### Lint the rule set

   `POST /api/v1/risk-profiles/validate` parses a rule array and returns a
   verdict. It never touches the database and always answers 200, so it is the
   cheapest way to check a rule shape before you commit to it.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/risk-profiles/validate \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{"kind":"trader","rules":[{"type":"max_leverage","value":20}]}'
   # → { "valid": true, "ruleCount": 1 }
   ```
   ```ts
   const verdict = await sdk.riskProfiles.validate(
     [{ type: 'max_leverage', value: 20 }],
     'trader',
   )
   ```
2. ### Create the profile

   `name` and `kind` are the only required fields. `kind` is one of `trader`,
   `challenge` or `funded` — a prop template can bind only the latter two.
   `enforcement` is `off`, `soft` or `hard`; `rules` holds up to 64 entries.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/risk-profiles \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{
       "name": "Retail default",
       "kind": "trader",
       "enforcement": "hard",
       "rules": [
         { "type": "max_leverage", "value": 20 },
         { "type": "max_concentration_pct", "value": 0.3 }
       ]
     }'
   # → 201 { "profile": { "id": "…", "kind": "trader", … } }
   ```
   ```ts
   const { profile } = await sdk.riskProfiles.create({
     name: 'Retail default',
     kind: 'trader',
     enforcement: 'hard',
     rules: [
       { type: 'max_leverage', value: 20 },
       { type: 'max_concentration_pct', value: 0.3 },
     ],
   })
   ```
3. ### Bind it to an account

   Binding writes `accounts.risk_profile_id` and busts the resolver cache, so
   the next order composes against the new rules.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/risk-profiles/$PROFILE_ID/bind \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{"accountId":"'"$ACCOUNT_UUID"'"}'
   # → { "ok": true, "accountId": "…", "riskProfileId": "…" }
   ```
   ```ts
   await sdk.riskProfiles.bind(profile.id, accountUuid)
   ```
   :::caution[`accountId` is the UUID, not the account number]
   The bind matches on `accounts.id`. Passing a human account number such as
   `100042` returns 404 `account_not_found`, even though the account reads fine
   at `GET /api/v1/accounts/100042`.
   :::

4. ### Swap or remove the binding

   An account holds exactly one profile. Binding a second profile replaces the
   first; binding the literal id `none` clears it.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/risk-profiles/none/bind \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{"accountId":"'"$ACCOUNT_UUID"'"}'
   ```
   ```ts
   await sdk.riskProfiles.unbind(accountUuid)
   ```
   :::note[`trader` profiles are advisory in production today]
Risk-profile enforcement ships off (`ENFORCE_RISK_PROFILES` defaults `false`),
so a bound `trader` profile annotates rather than blocks. Challenge and funded
programs are unaffected: a prop account is judged by the rule snapshot on
`prop_accounts`, which is always enforced.
:::

**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/) ·
[Scopes & tiers](https://docs.troncharts.xyz/docs/auth/scopes/)