Skip to content

Launch a prop firm

Publish challenge templates, deploy your PropInstance contract, take challenge deposits, and approve payouts. Everything here is self-serve over a firm:operate credential.

Time to live: about ten minutes of API calls, once your multisig is ready.

Prerequisite Why
A firm:operate credential Every call here is scoped by it. See Credentials & tokens.
A multisig address (0x…, 40 hex) Owns your deployed PropInstance. Custody of firm funds stays there — the platform never holds it.
A fee config entryFeeBps and payoutFeeBps are required; forfeiture BPS are optional.
create ──▶ readiness ──▶ deploy ──▶ go-live ──▶ [traders trade] ──▶ approve payouts
pending_deploy (dry-run) active
Terminal window
curl -s https://api.troncharts.xyz/api/auth/api-token \
-H 'content-type: application/json' \
-d '{"apiKey":"'"$TC_API_KEY"'","apiSecret":"'"$TC_API_SECRET"'"}'
# → { "token": "eyJ…", "tier": "…", "expiresAt": <epoch ms>, … }

The mint endpoint is one of the few that resolves without a tenant. Every /api/v1/* call below must also say which tenant it is for: send X-Tenant-Slug: <your-slug>, or call from an origin registered on your tenant. Miss it and you get 404 {"error":"unknown_tenant_origin"}. See Tenancy.

The firm starts in pending_deploy. Your tenant comes from the token — you cannot create a firm for anyone else.

Terminal window
curl -s https://api.troncharts.xyz/api/v1/firms \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG" \
-H 'content-type: application/json' \
-d '{
"name": "Acme Funded",
"multisigAddress": "0xYourGnosisSafe000000000000000000000000",
"feeConfig": { "entryFeeBps": 500, "payoutFeeBps": 1000 },
"chainId": 42161,
"supportEmail": "support@acmefunded.com",
"withPaperShadow": true
}'
# → { "firm": { "id": "firm_…", "status": "pending_deploy", … },
# "paperShadowId": "…", "seededTemplates": [ … ] }

withPaperShadow: true (the default) seeds a paper twin of the firm so you can rehearse the whole trader experience with no real money.

SDK sdk.firms.create({ name, multisigAddress, feeConfig, chainId }) · MCP create_firm

Readiness is your go-live checklist. Poll until ready: true.

Terminal window
curl -s "https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/readiness?target=mainnet" \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG"
# → { "ready": false, "checks": [
# { "key": "factory_configured", "ok": true, "required": true },
# { "key": "observer_configured", "ok": true, "required": true },
# { "key": "deployer_configured", "ok": true, "required": false },
# { "key": "observer_whitelisted", "ok": true, "required": true },
# { "key": "instance_deployed", "ok": false, "required": true },
# { "key": "watcher_subscribed", "ok": false, "required": true },
# { "key": "vault_funding_configured", "ok": false, "required": false } ] }
Check Cleared by
factory_configured, observer_configured, observer_whitelisted The platform (shared rails)
instance_deployed, watcher_subscribed You, by deploying in step 4
deployer_configured The platform. This is the gas-only deployer key the step-4 broadcast signs with.
vault_funding_configured The platform. Only firm-wallet (firm-custody) programs need it — it is unrelated to the deployer.

SDK sdk.firms.readiness(firmId, 'mainnet') · MCP check_firm_readiness

Always dry-run first — it runs the full preflight without broadcasting.

Terminal window
# Preflight only
curl -s https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/deploy \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG" \
-H 'content-type: application/json' \
-d '{ "target": "mainnet", "dryRun": true }'
# Broadcast — gas-only platform deployer; the instance is owned by your multisig
curl -s https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/deploy \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG" \
-H 'content-type: application/json' \
-d '{ "target": "mainnet", "dryRun": false }'
# → { "deployed": true, "instanceAddress": "0x…", "txHash": "0x…",
# "chainId": 42161, "whitelisted": true }

SDK sdk.firms.deploy(firmId, { target, dryRun }) · MCP deploy_prop_instance

Terminal window
curl -s https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/status \
-X PATCH \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG" \
-H 'content-type: application/json' \
-d '{ "status": "active" }'

status accepts pending_deploy | active | paused | migrated. Pause any time to stop new challenges without tearing down the instance.

SDK sdk.firms.setStatus(firmId, 'active') · MCP set_firm_status

Your traders — not your operator credential — drive this half:

  1. Start a challengePOST /api/v1/prop-accounts { templateId } creates a pending_payment account against one of your templates.
  2. Pay the entry fee — the trader’s wallet signs the deposit calldata from POST /api/prop/deposit-calldata { propAccountId, amountUsdcUnits }. Funds go straight to your PropInstance; the claim watcher flips the account to active.
  3. Trade — the account trades against your risk rules.

You manage what they trade against with your prop:manage scope: /api/v1/prop-templates/*, /api/v1/risk-profiles/*, /api/v1/trading-groups/*. See Prop account model.

When a trader passes, the payout lands in your worklist. Approving records and queues it — settlement is dispatched against your instance. You are the authorisation, not the on-chain sender.

Terminal window
# Who's owed
curl -s https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/payouts/pending \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG"
# Decide
curl -s https://api.troncharts.xyz/api/v1/firms/$FIRM_ID/payouts/$PROP_ACCOUNT_ID/decision \
-H "authorization: Bearer $TOKEN" \
-H "x-tenant-slug: $TC_TENANT_SLUG" \
-H 'content-type: application/json' \
-d '{ "decision": "approved", "amountUsd": 4200, "reason": "passed evaluation" }'

SDK sdk.firms.decidePayout(firmId, propAccountId, { decision, amountUsd }) · MCP approve_payout

The MCP server ships a scripted launch-firm prompt that runs create → readiness → dry-run deploy → broadcast → re-check → go-live. It needs the stdio server built from the monorepo — the firm-lifecycle tools it calls are not on the Streamable HTTP endpoint. See MCP server and Build with an AI agent.

Every field and response shape is in the REST reference under /api/v1/firms/*. Fee BPS, forfeiture rules, and status transitions are authoritative there.