# Go-live checklist

Every item here is something that has actually bitten an integration. Each one
names the endpoint or frame that proves it, so this is a checklist you can
execute rather than nod at.

## Credentials

- [ ] The credential you ship with holds the **narrowest tier** that does the
      job. A reporting integration does not need `fullTrading`; a risk system
      that only flattens needs `liquidation`. `GET /api/v1/discovery` reports the
      tier for the ~40 endpoints it catalogs, but no scopes and not the whole
      surface — it cannot confirm a credential carries `firm:operate`,
      `prop:manage`, or `tenant:config`. Check the rest against the
      [OpenAPI spec](https://docs.troncharts.xyz/docs/reference/specs/).
- [ ] Your secret is **not** in the repository, the bundle, or a log line. It is
      shown once at issuance.
- [ ] You have exercised **rotation** — issue a second credential, deploy it,
      revoke the first — at least once, before you need to do it under pressure.
- [ ] A revoked token fails the way you expect: revoke one deliberately and
      watch your error path.

## Orders

- [ ] Every order write sends an **`Idempotency-Key`**. A network timeout on
      placement is indistinguishable from a rejection, and a retry without a key
      can double a position.
- [ ] You track orders by **intent id**, and take the terminal state from
      [`/ws/risk`](https://docs.troncharts.xyz/docs/realtime/risk/) rather than polling.
- [ ] `venue` and `symbol` come from `GET /api/v1/venues` and
      `GET /api/v1/symbols/{venue}`, not from constants. Both change per
      deployment without an API version bump.
- [ ] Sizes and prices are sent as **decimal strings**. `0.1` does not survive a
      JSON double round-trip exactly.
- [ ] You have handled `422` — the venue rejected it — distinctly from `400`.
      They mean different things and usually need different retries.

## Streams

- [ ] You send **`Subscribe { topics: [...] }`** after `Authenticated`.
      `/ws/risk` pushes nothing until you do, and `topics` must be non-empty.
- [ ] You assert **`seq` contiguity** on `/ws/risk` and re-snapshot on a gap.
      Frames coalesce under load; a gap is normal, not exceptional.
- [ ] You handle **`Resync-Required`** by dropping local state, not by ignoring
      it.
- [ ] Your heartbeat sends `Ping` every 30s. The socket idles out after 60s of
      inbound silence.
- [ ] You seed correctly: **subscribe first**, then fetch the REST snapshot,
      then apply buffered updates. The other order leaves a hole.
- [ ] Reconnect **mints a fresh bootstrap token, re-authenticates,
      re-`Subscribe`s to its topics, and re-requests state** rather than
      assuming the cached view survived. Topics live on the connection, so a
      new socket starts with an empty topic set and stays silent until it
      subscribes.

## Money and reconciliation

- [ ] You compute volume from **fills** and P&L from **trades**. Mixing them is
      the most common reconciliation bug.
- [ ] You know whether your accounts are **routed or internalised**, and you
      discriminate on the **account**, never on `venue: "paper"` — that sentinel
      covers both the sandbox and internalised real-money accounts. See
      [the execution model](https://docs.troncharts.xyz/docs/auth/trust-model/#execution-routed-vs-internalised).
- [ ] You do **not** build exchange-side reconciliation on `venueOrderId` for
      internalised rows; those ids are ours and resolve nowhere off-platform.

## Webhooks, if you use them

- [ ] Your receiver **verifies `x-webhook-signature`** — HMAC-SHA256 over the
      raw body bytes, hex-encoded and `sha256=` prefixed, compared in constant
      time — before parsing the body, and checks `x-api-key` in addition, not
      instead. The signature is the provenance proof; `x-api-key` is a static
      bearer that anyone who captures one delivery can replay. See
      [Webhooks](https://docs.troncharts.xyz/docs/realtime/webhooks/#writing-a-receiver).
- [ ] It **deduplicates on `eventId`** — delivery is at-least-once.
- [ ] It returns **200 fast**, then works. A slow receiver looks like a failing
      one and earns a retry.
- [ ] You know a dead endpoint **auto-disables** after the retry budget
      (1s → 24h), and that re-enabling it is an operator action — so you have a
      way to notice, and someone to ask.

## Prop firms

- [ ] You have driven a challenge to a **breach** on the paper twin and watched
      the account go terminal and positions flatten.
- [ ] You have driven one to a **pass** and confirmed no money moves at pass —
      only the payout ceiling is recorded.
- [ ] You have worked a payout **decision** end to end.
- [ ] Readiness reports `ready: true` **and** `deployer_configured` is green —
      `ready` aggregates only the `required` checks and ignores that one, but a
      live broadcast without it returns `503 deployer_unconfigured`. Dry-run
      deploy before broadcasting; the dry run returns before the deployer key is
      ever checked, so it cannot catch this for you.

## The last one

- [ ] Do all of the above against a
      [sandbox account](https://docs.troncharts.xyz/docs/sandbox/paper-accounts/) first, then repeat the
      order path with the **smallest possible real size** before you scale it.
      The sandbox proves your code; a small live order proves your assumptions
      about fills.