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.
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.
-
Send the challenge, and say which firm — or don’t
Section titled “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 herefirmId— reuse that one- neither — your tenant’s single live firm, resolved server-side
Most tenants run one firm, so
firmIdhad exactly one legal value and fetching it fromGET /api/v1/firmswas 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
firmandfirmIdis still 400invalid_body. Omitting when your tenant owns no live firm is 403firm_not_found; when it owns several, 400firm_ambiguous, and thedetailnames them so you can pick.challengeneedsnameandaccountSizeUsd; everything else has a default, so the smallest possible body is:{ "challenge": { "name": "Acme 25K Evaluation", "accountSizeUsd": "25000.00" } }The example below creates the firm too, which is the first-run case.
Terminal window 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, … } }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 -
Loss and profit limits are percentages; position limits are not
Section titled “Loss and profit limits are percentages; position limits are not”maxDrawdownPct: 10on a25000.00account is stored asmax_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 maxDrawdownPct10max_drawdown_usddailyLossPct5daily_loss_cap_usdprofitTargetPct10profit_target_usd, challenge only (refused withkind: "funded")minTradingDaysunset min_trading_days, only when setmaxPositionSizeunset max_position_size, stored as sent, only when setmaxTotalContractsunset max_total_lots_open, stored as sent, only when setmaxLeverageunset max_leverage, stored as sent, only when setmaxPositionSizeis a count of units per symbol, not USD:2allows 2 BTC onBTC.HLand 2 DOGE onDOGE.HLalike, resting orders included.maxTotalContractsis the same count summed over every symbol of the account. On B3 a unit is a contract; on.FUTfutures it is a mini-equivalent contract, summed over the product family (10 MES = 1 ES). For a notional cap usemaxLeverage, which is gross open exposure ÷ equity —1keeps 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.Restrict venues with
venues: ["hyperliquid", "aster"], which also lands as anallowed_venuesrule on the created group. Omit the field for no restriction — an empty array is not the same thing and is rejected. -
Call it a second time
Section titled “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’snextStepTemplateId. A funded step gets no profit target — meeting one would end the funded account — so leaveprofitTargetPctout 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 409already_exists, before anything is created — so a blind retry of a call that succeeded creates no twin. Sendchallenge.idto 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— sendgroupSlug.Reuse the firm you already made rather than creating a twin. If it is your tenant’s only live firm, say nothing at all:
Terminal window 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_ambiguousthere, never resolved by guesswork:Terminal window 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" }}'
When to use the long way instead
Section titled “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 — change rules, fees, the tier ladder
- Create a trading group and add an account — venue scope, leverage, fee schedule
- Create a risk profile and bind it — share one rule set across templates
Next: Sell a paper prop account from a template · Launch a prop firm · How an account is configured