# Stand up a challenge in one call

The long way is four requests across two scopes, each handing you an id the next
one needs. `POST /api/v1/prop/setup` runs the whole chain and returns every id
it created. When this is done you have an `active` template you can sell as a
[paper prop account](https://docs.troncharts.xyz/docs/recipes/sell-a-paper-challenge/).

This is a shortcut *through* the gates, not around them: creating a firm needs
`firm:operate`, exactly as `POST /api/v1/firms` does, and reusing or deriving
one needs only `prop:manage`.

1. ### Send the challenge, and say which firm — or don't

   Three ways to name the firm, and the third is the common one:

   - `firm` — create one here
   - `firmId` — reuse that one
   - **neither** — your tenant's single live firm, resolved server-side

   Most tenants run one firm, so `firmId` had exactly one legal value and
   fetching it from `GET /api/v1/firms` was a round-trip that only ever went
   wrong one way: sending the *tenant* id where the *firm* id goes. Omitting it
   removes both the round-trip and the mistake.

   Sending **both** `firm` and `firmId` is still 400 `invalid_body`. Omitting
   when your tenant owns no live firm is 403 `firm_not_found`; when it owns
   several, 400 `firm_ambiguous`, and the `detail` names them so you can pick.

   `challenge` needs `name` and `accountSizeUsd`; everything else has a default,
   so the smallest possible body is:

   ```json
   { "challenge": { "name": "Acme 25K Evaluation", "accountSizeUsd": "25000.00" } }
   ```

   The example below creates the firm too, which is the first-run case.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/prop/setup \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{
       "firm": { "name": "Acme Prop" },
       "challenge": {
         "name": "Acme 25K Evaluation",
         "accountSizeUsd": "25000.00",
         "feeUsd": "199.00",
         "payoutSplitPct": "80"
       }
     }'
   # → 201 { "firmId": "…", "riskProfileId": "…", "tradingGroupId": "…",
   #         "templateId": "acme-25k-evaluation-challenge-3f9a1c", "template": { "active": true, … } }
   ```
   ```ts
   const setup = await ops.propSetup.run({
     firm: { name: 'Acme Prop' },
     challenge: {
       name: 'Acme 25K Evaluation',
       accountSizeUsd: '25000.00',
       feeUsd: '199.00',
       payoutSplitPct: '80',
     },
   })
   // setup.templateId → sell it; setup.firmId → reuse it for the next challenge
   ```
   :::note[`multisigAddress` is optional]
   Without one the firm is created `pending_deploy`. It can be configured and
   paper-traded immediately; only `POST /api/v1/firms/{id}/deploy` refuses,
   with `tenant_config_incomplete`, until you set one. That is what lets you
   walk the whole flow before you have a wallet.
   :::

2. ### Loss and profit limits are percentages; position limits are not

   `maxDrawdownPct: 10` on a `25000.00` account is stored as
   `max_drawdown_usd: 2500`. The stored rules are absolute USD because that is
   what the evaluator reads — doing the multiplication yourself was a step that
   served the storage format.

   | Field | Default | Becomes |
   | --- | --- | --- |
   | `maxDrawdownPct` | `10` | `max_drawdown_usd` |
   | `dailyLossPct` | `5` | `daily_loss_cap_usd` |
   | `profitTargetPct` | `10` | `profit_target_usd`, challenge only (refused with `kind: "funded"`) |
   | `minTradingDays` | unset | `min_trading_days`, only when set |
   | `maxPositionSize` | unset | `max_position_size`, stored as sent, only when set |
   | `maxTotalContracts` | unset | `max_total_lots_open`, stored as sent, only when set |
   | `maxLeverage` | unset | `max_leverage`, stored as sent, only when set |

   `maxPositionSize` is a count of **units per symbol**, not USD: `2` allows
   2 BTC on `BTC.HL` and 2 DOGE on `DOGE.HL` alike, resting orders included.
   `maxTotalContracts` is the same count summed over **every** symbol of the
   account. On B3 a unit is a contract; on `.FUT` futures it is a
   mini-equivalent contract, summed over the product family (10 MES = 1 ES).
   For a notional cap use `maxLeverage`, which is gross open exposure ÷ equity —
   `1` keeps exposure at or below the account's equity.

   The two contract limits are checked **when the order is placed**: an order
   that would take the count above the cap is refused with
   `400 above_max_open_contracts`, working orders counted as if they filled.
   Reduce-only orders and orders that do not grow the count always pass. The
   other rules are monitored by the risk engine — see
   [Where rules are enforced](https://docs.troncharts.xyz/docs/launch/prop-accounts/#where-rules-are-enforced).

   Restrict venues with `venues: ["hyperliquid", "aster"]`, which also lands as
   an `allowed_venues` rule on the created group. Omit the field for no
   restriction — an **empty array is not the same thing** and is rejected.

3. ### Call it a second time

   The template id defaults to
   `<slug of challenge.name>-<challenge.kind>-<challenge.step>-<tenant tag>`.
   Because kind and step are part of it, every phase of one program can share a
   name: call it once per phase — `"kind": "challenge", "step": 1`, then
   `"step": 2`, then `"kind": "funded"` — and link them in order with each
   template's `nextStepTemplateId`. A funded step gets no profit target —
   meeting one would end the funded account — so leave `profitTargetPct` out of
   that call. Note that a later-phase template is still listed and buyable on
   its own. Only the same name, kind **and** step again on your
   tenant is refused with 409 `already_exists`, before anything is created — so
   a blind retry of a call that succeeded creates no twin. Send `challenge.id`
   to sell that combination twice.

   The trading-group slug and the risk profile's name are derived from the same
   name but are disambiguated for you, so they never need a field of their own.
   If the derived slug and its numbered variants are all taken you get 409
   `group_slug_taken` — send `groupSlug`.

   Reuse the firm you already made rather than creating a twin. If it is your
   tenant's only live firm, say nothing at all:

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/prop/setup \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{
       "challenge": { "name": "Acme 50K Evaluation", "accountSizeUsd": "50000.00" }
     }'
   ```

   Name it explicitly when you run more than one firm — omitting is refused
   with 400 `firm_ambiguous` there, never resolved by guesswork:

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/prop/setup \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{
       "firmId": "'"$FIRM_ID"'",
       "challenge": { "name": "Acme 50K Evaluation", "accountSizeUsd": "50000.00" }
     }'
   ```

:::caution[Partial failure rolls back — except sometimes the firm]
Nothing this call created survives a failure: the risk profile, the trading
group and — when `firm` created one — the firm, its paper shadow and its seeded
catalog are all rolled back.

If the firm could *not* be rolled back, the error carries `firmId` +
`firmCreated: true`. That firm really is still there: retry with `firmId`
rather than creating a twin.
:::

## When to use the long way instead

`prop/setup` creates. It does not edit. Once the objects exist, go to the
resource endpoints — they are the same objects:

- [Create a challenge template](https://docs.troncharts.xyz/docs/recipes/create-a-challenge-template/) — change rules, fees, the tier ladder
- [Create a trading group and add an account](https://docs.troncharts.xyz/docs/recipes/create-a-trading-group/) — venue scope, leverage, fee schedule
- [Create a risk profile and bind it](https://docs.troncharts.xyz/docs/recipes/create-a-risk-profile/) — share one rule set across templates

**Next:** [Sell a paper prop account from a template](https://docs.troncharts.xyz/docs/recipes/sell-a-paper-challenge/) ·
[Launch a prop firm](https://docs.troncharts.xyz/docs/launch/prop-firm/) ·
[How an account is configured](https://docs.troncharts.xyz/docs/launch/account-configuration/)