# 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.

## What you need first

| Prerequisite | Why |
| --- | --- |
| A **`firm:operate` credential** | Every call here is scoped by it. See [Credentials & tokens](https://docs.troncharts.xyz/docs/auth/credentials/). |
| 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. |

:::note[Shared rails are pre-provisioned]
The factory, the settlement observer, and its whitelist are platform rails.
They appear as readiness checks in step 3 — you don't configure them, but you
do wait for them to go green before you deploy.
:::

## The lifecycle

```
create ──▶ readiness ──▶ deploy ──▶ go-live ──▶ [traders trade] ──▶ approve payouts
pending_deploy         (dry-run)    active
```

## 1. Authenticate

```bash
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](https://docs.troncharts.xyz/docs/auth/tenancy/).

## 2. Create the firm

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

```bash
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`

## 3. Wait for readiness

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

```bash
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. |

:::caution[`ready: true` does not mean the deploy will succeed]
`ready` aggregates only the checks marked `required`, and both
`deployer_configured` and `vault_funding_configured` are reported as
not-required. But a broadcast (`dryRun: false`) with `deployer_configured`
red returns `503 deployer_unconfigured`. Check that key yourself before step 4,
and ask the platform if it is red.
:::

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

## 4. Deploy your PropInstance

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

```bash
# 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`

## 5. Go live

```bash
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`

## 6. Traders enter and trade

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

1. **Start a challenge** — `POST /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](https://docs.troncharts.xyz/docs/launch/prop-accounts/).

## 7. Approve payouts

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.

```bash
# 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`

## Do it all with one prompt

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](https://docs.troncharts.xyz/docs/sdks/mcp/) and
[Build with an AI agent](https://docs.troncharts.xyz/docs/sdks/ai-agents/).

## Full contract

Every field and response shape is in the [REST reference](https://docs.troncharts.xyz/docs/reference/rest/)
under `/api/v1/firms/*`. Fee BPS, forfeiture rules, and status transitions are
authoritative there.