# Create a paper account to test against

One call provisions a paper account with a starting balance and a seeded position,
so your first reads return real data and your first orders run the real pipeline
without real money.

1. ### Provision the account

   `POST /api/v1/sandbox/paper-account`. Every field is optional and `{}` is a valid
   body — the defaults are `baselineUsd` `"50000"`, `symbol` `"BTC.HL"` and
   `seedDemoPosition` `true`. The credential must be `fullTrading`; this route
   compares the tier for exact equality, so `readonly` and `liquidation` both get
   `403 tier_insufficient`. No `prop:manage` scope is involved.

   :::caution[Off by default]
   `SANDBOX_PROVISIONING_ENABLED` defaults to `'false'` and the route returns a bare
   `404 not_found` until an operator turns it on. A 404 here means the flag, not a
   wrong path.
   :::

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/sandbox/paper-account \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -d '{"baselineUsd":"50000","symbol":"BTC.HL","displayName":"integration rehearsal"}'
   # → 201 { accountId, accountNumber, propTenantId, templateId,
   #         mode: "paper", demoPositionSeeded: true, ws: { hint } }
   ```
   ```ts
   const sandbox = await sdk.sandbox.paperAccount({
     baselineUsd: '50000',
     symbol: 'BTC.HL',
     displayName: 'integration rehearsal',
   })
   const accountId = sandbox.accountId
   ```
   The new account is linked into the calling credential's ownership scope, so it
   shows up in `GET /api/v1/accounts` and you can address it by `accountId` from
   here on. Field-by-field detail lives on
   [Paper accounts](https://docs.troncharts.xyz/docs/sandbox/paper-accounts/).

2. ### Read the seeded state back

   The account is funded and holding the demo position the moment the 201 lands.
   `/state` returns balance and equity per venue; `/positions` returns what it
   holds.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/accounts/$ACCOUNT_ID/state \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG"
   # → { accountId, accountNumber,
   #     perVenue: [ { venue, exchange, equityUsd, balanceUsd, marginUsedUsd,
   #                   marginAvailableUsd, unrealizedPnlUsd, … } ] }
   ```
   ```ts
   const state = await sdk.accounts.state(accountId)
   const { positions } = await sdk.accounts.positions(accountId)
   console.log(state.perVenue, positions.length)
   ```
3. ### Point your order flow at it

   Send the sandbox `accountId` in the OMS body. It is optional on
   `POST /api/v1/oms/intents` and defaults to the session's active account, which a
   bearer credential does not have — so name it explicitly. Use `"venue": "paper"`;
   against a non-paper account that is rejected `400 paper_venue_requires_paper_account`.

   ```bash
   curl -s https://api.troncharts.xyz/api/v1/oms/intents \
     -H "authorization: Bearer $TOKEN" \
     -H "x-tenant-slug: $TC_TENANT_SLUG" \
     -H 'content-type: application/json' \
     -H "idempotency-key: $(uuidgen)" \
     -d '{"accountId":"'"$ACCOUNT_ID"'","venue":"paper","symbol":"BTC.HL",
          "side":"buy","type":"market","qty":"0.01"}'
   ```

   The paper engine matches synchronously, so a market order comes back as
   `201 { intentId, state: "filled", syncFill: { qty, avgPrice } }`. The full body
   schema is on [Orders & OMS](https://docs.troncharts.xyz/docs/trading/orders/).

**Next:** [Place your first order](https://docs.troncharts.xyz/docs/recipes/place-your-first-order/) · [Read balance, positions and open orders](https://docs.troncharts.xyz/docs/recipes/read-account-state/) · [Stream account updates](https://docs.troncharts.xyz/docs/recipes/stream-account-updates/)