> ## Documentation Index
> Fetch the complete documentation index at: https://partners.docs.polymarket.us/llms.txt
> Use this file to discover all available pages before exploring further.

# Regulatory

> The ten reports, the NFA/CFTC demo, the risk-monitoring overview, and the record rules that make NFA reports error.

Two things move an IB's go-live date more than any engineering work: the reporting pack, and three record-level mistakes that break NFA reports after the record is expensive to change.

<Info>
  Prerequisites:

  * the `read:reports` and `read:cash-movements` scopes granted against your client — ask for both at
    the scopes gate, not later. See [Path to production](/ib/start#path-to-production),
  * [Funds handling](/funds) — several reports are balance reports and the balance fields need reading
    carefully,
  * your firm strings and your IB anchor DCO account — see [Firms, participants and
    accounts](/identity).
</Info>

## Reporting pack

Start this in week one. **Several of the ten reports below have no platform source today**, and the gap between "we can pull that" and "nobody has built that" is what moves an IB's go-live date.

ISVs never ask for this pack. It exists because your registration requires it, so the questions below arrive in our channel from IBs and nowhere else.

### The pack

Five pieces, of which the reports are the largest:

1. The **NFA/CFTC demo** of your system.
2. **Flow of customer funds** — where money sits at each step, which for you is constrained by the prohibition on accepting or holding customer funds.
3. A **back and middle office overview**.
4. A **risk-monitoring overview** — intraday, overnight, limits and thresholds, and liquidation.
5. **Ten named reports.**

### The ten reports

"What we can supply today" below is the platform surface, not a report product. **We do not produce these reports for you**; where a source exists, you build the report from it.

| Report                            | What we can supply today                                      | How                                                                                                                                                                                                                                                                                                                         |
| --------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Customer Statements — daily**   | `VERIFY` — no statement product exists on the partner surface | Buildable from the per-account balance ledger plus drop copy fills for the traded day. You compose and render it                                                                                                                                                                                                            |
| **Customer Statements — monthly** | `VERIFY` — same                                               | Same source, month-scoped. Note the retention hazard below: pre-maintenance execution queries return empty                                                                                                                                                                                                                  |
| **Open Position / Open Interest** | Positions: yes. **Open interest: `VERIFY`**                   | `CreatePositionChangeSubscription` carries position deltas and is resumable. Open interest across the market is not a documented partner surface                                                                                                                                                                            |
| **Margin Call / Debit**           | Partial, and the fields do not agree                          | `GetAccountBalance` returns `marginRequirement`, `capitalRequirement` and `excessCapital` for a **firm** account. It returns `InvalidArgument: invalid account` for a **participant clearing account**. Whether an open short carries a collateral requirement is unanswered — see the warning below                        |
| **Moneyline**                     | `VERIFY` — not evidenced on the partner surface               | Nothing in the verified ledger names a Moneyline report or the fields it needs. Ask before you promise a format                                                                                                                                                                                                             |
| **Equity Run / Account Status**   | Fields, not a run                                             | `GetAccountBalance` (`balance`, `capitalRequirement`, `excessCapital`, `buyingPower`, `unsettledFunds`, `marginRequirement`) plus `GetFundingAccountBalance` for the pool. The per-account read fails on participant clearing accounts, so partners derive the figure from the newest balance-ledger entry's `afterBalance` |
| **Cash Blotter**                  | Yes, with caps                                                | The balance-ledger stream per account, plus **ledger CSV downloads at \~5/min per firm**. `LedgerEntryType` has an allowlist: twelve internal types are suppressed (`NETTING`, `GIVE_UP`, `INTEREST`, `SETTLEMENT_FEE`, …) and requesting one returns `Aborted` / 409                                                       |
| **Reconciliation**                | Yes                                                           | Drop copy is the source of record for fills and commissions and its `resume_token` is populated (\~576 bytes). `POST /v1/report/trades/search` **with a symbol filter** returns in \~130 ms. See the scales warning below                                                                                                   |
| **Large Trader**                  | `VERIFY` — not evidenced on the partner surface               | No large-trader reporting surface, threshold or field is in the verified ledger                                                                                                                                                                                                                                             |
| **Trade balancing**               | `VERIFY` — not evidenced on the partner surface               | Drop copy plus the trades search give you both sides of your own activity; a balancing report format is not defined                                                                                                                                                                                                         |

<Warning>
  **Five of the ten have no evidenced platform source.**&#x20;
  Take the five marked `VERIFY` to your integration lead as a written list in week one, with the
  format your regulator expects for each. Do not assume a report exists because it is named in the
  pack — the pack names what you must produce, not what we hand you.
</Warning>

<Warning>
  **Partner-facing funding APIs for embedded partners are reported not to have been live when an IB
  asked for them.**&#x20;
  What *is* recorded, and is adjacent: the daily **Vendor Fees report has no delivery mechanism**
  ("Delivery method TBD"), the **firm-level balance-ledger stream is "coming soon" with no date**, and
  there is **no bulk-export path and no async report job** on the reporting surface. Confirm
  availability before you commit a delivery date to a regulator.
</Warning>

### Reporting mechanics that break IB reports specifically

These are the four that produce tickets.

**`POST /v1/report/trades/search` unfiltered hits the 30-second CloudFront 504 about two thirds of the time.** The same call with a symbol filter returns in \~130 ms, reliably. There is no documented bulk-export path, no published window limit and no async report job, so a month-end pull is a loop over symbols, not one call.

**Stored execution history is archived during maintenance, and pre-maintenance execution queries return empty.** This is a material retention fact that appears only in the changelog. Maintenance is not the stable window the FAQ describes: August–September 2026 ran near-weekly one-off windows at 2–8am, 3–7am, 4–7am and 5–8am ET. **Pull and persist your own copy of every traded day's executions on the day.** If your statement generation reaches back across a maintenance window, it can come back empty rather than wrong, which is harder to notice.

**`GetTradeStats` does not behave the way a reporting engineer expects.** `bars` is a **divisor, not an interval** — 6 bars over 14 days gives 2.8-day buckets. Empty buckets are **returned, not omitted**, as an all-zero object, so **treat zero as "no trades", never as a print at price 0.** `cleared_*` counterparts reflect post-clearing state and **read zero for recently executed trades**. Use `where_clause`, not `field_filter`: `field_filter` validates against a fixed column allowlist that excludes `update_time` and `create_time`, so a time cursor there returns `InvalidArgument: field not allowed`. Page tokens are **offset-based, so dedupe on `symbol`** when sweeping pages.

**The push channel does not scale to a customer book.** The balance-ledger stream is **per-account and counts against the 20-concurrent-streams-per-firm cap**, so it cannot be run per customer. The firm-level ledger stream is "coming soon" with no date. That leaves no scalable push channel for `RESOLUTION` entries at scale — poll and reconcile instead.

<Snippet file="scales-warning.mdx" />

The reconciliation mistake this causes is specific: **the most common one is using raw `order_qty` as the contract count.** On an instrument with `fractional_quantity_scale = 100` that overstates the fee 100×. Instruments with scale `1` make the naive arithmetic accidentally correct, so the error often surfaces only on the first fill in a scale-100 market. One dollar is `priceScale × fractionalQtyScale` notional units: when both are 100, `commission_notional_collected = 100` means **$0.01, not $1.00**.

<Warning>
  **There is no fee-schedule endpoint, and the taker coefficient has changed.**

  `Fee = Θ × C × p × (1 − p)`, rounded to the cent with banker's rounding. **`Θ` changed from 0.06 to
  0.0695 on 2026-09-14** and every partner hand-edited their systems, because no endpoint publishes
  it. Put the coefficient in configuration, not in code, and reconcile fees against drop copy rather
  than recomputing them from a constant you trust.
</Warning>

Also relevant to any report that reaches settlement: **`RESOLUTION` ledger entries may be gross with `COMMISSION` deducted separately, or net, and that is unanswered.** Executions fold commission into a single net `ORDER_EXECUTION` entry, so do not assume settlement behaves the same way.

### Risk-monitoring overview

The pack requires you to describe four things: **intraday risk, overnight risk, limits and thresholds, and liquidation.**

<Warning>
  **The platform side of all four is unpublished.**&#x20;
  Nothing in the verified ledger describes intraday or overnight risk monitoring, a limits-and-
  thresholds surface, or a liquidation mechanism for a participant account. **Do not describe a
  platform control that has not been confirmed to you in writing** — an NFA reviewer will ask where it
  is documented.
</Warning>

Three constraints you can state from the platform's actual behaviour, because they shape your own risk model:

* **Your order surface is fill-or-kill only and you are always the taker.** You cannot rest an order, so you cannot manage risk by working an order; every order is prefunded with an exact atomic transfer and either crosses or expires.
* \*\*An open short carries an obligation of up to $1.00 per contract**, and `balance_reservation` and `margin_requirement` came back **empty** on the ledger entries for one. Hold back $1.00 per open short contract in your own model until the field is named.
* **Day orders cancel at the traded-day roll** as of 2026-09-13 (v0.0.89); GTD is the alternative. This does not apply to the fill-or-kill partner surface, but it applies to any generic order-entry question your reviewer asks about.

### The NFA demo

The demo requirement is real and the environment question is not settled, because **preprod cannot demonstrate a settled market.**

| Constraint                                                                                                                                                                                          | Consequence for a demo                                                                                      |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Markets do not resolve in preprod.** Instruments reach `INSTRUMENT_STATE_EXPIRED` in batches — 363, 455, 544 and 549 in one session — with none resolving                                         | You cannot demonstrate resolution, settlement credit, or a customer statement containing a settled position |
| **Books are thin and intermittent.** `pmsim` is the only liquidity provider; coverage runs \~48% of `astatc-` symbols down to \~0.1% of `aec-atp`; depth sits at the edges ($0.98 offer, $0.06 bid) | A fill-or-kill order may not cross at all during your demo window                                           |
| **No preprod status page, health endpoint or maintenance calendar**                                                                                                                                 | If preprod is degraded mid-demo you cannot show that it is ours                                             |
| **Preprod pools are funded manually by us on a Slack request**                                                                                                                                      | Request funding days ahead of a scheduled demo                                                              |

<Warning>
  **Which environment the NFA/CFTC demo runs in is not decided.**&#x20;
  Raise the date and the environment in your channel as soon as the demo is scheduled, and say in the
  first line that it is a regulatory deadline with a date. Do not schedule a demo that depends on a
  preprod settlement.
</Warning>

### What can go wrong

| Symptom                                               | Cause                                                                                                        | What you do                                                                                       |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| A month-end trades pull times out                     | `POST /v1/report/trades/search` unfiltered hits the 30-second CloudFront 504 about two thirds of the time    | Loop with a symbol filter; \~130 ms per call, reliably                                            |
| A statement for an earlier period comes back empty    | Stored execution history is archived during maintenance and pre-maintenance execution queries return empty   | Persist every traded day's executions on the day. Raise the affected dates                        |
| A fee figure in your reconciliation is out by 100×    | Raw `order_qty` used as the contract count on a `fractional_quantity_scale = 100` instrument                 | De-scale with both scales read off the instrument                                                 |
| A fee figure is out by 10×                            | A hard-coded divisor of 10,000 on an instrument publishing `priceScale: 1000` with `fractionalQtyScale: 100` | Read both scales per instrument. Two of 121 open ATP instruments are in this shape                |
| `Aborted` or 409 from a ledger query                  | You requested one of the twelve suppressed `LedgerEntryType` values                                          | Those types are not available to partners. Reconcile without them and say so in your report notes |
| A zero bucket read as a print at price 0              | `GetTradeStats` returns empty buckets as all-zero objects                                                    | Treat zero as "no trades"                                                                         |
| `InvalidArgument: field not allowed` on a time cursor | `field_filter`'s column allowlist excludes `update_time` and `create_time`                                   | Use `where_clause`                                                                                |
| Duplicate rows when sweeping report pages             | Page tokens are offset-based                                                                                 | Dedupe on `symbol`                                                                                |
| You cannot run a ledger stream per customer           | The balance-ledger stream is per-account and counts against the 20-streams-per-firm cap                      | Poll and reconcile. The firm-level stream is "coming soon" with no date                           |
| `InvalidArgument: invalid account` on a balance read  | `GetAccountBalance` fails for a participant clearing account and succeeds for a firm account                 | Derive from the newest balance-ledger entry's `afterBalance` and report the workaround            |

## Naming and record requirements

Three record-level mistakes break IB reporting, and all three are cheap to prevent and expensive to unpick after go-live. Check every production record against this section before your first customer.

### The legal name must be a physical person's name

**At the account level and at the user level, the legal name must be a physical person's name, or NFA reports error.** Not your company name, not a DBA, not a product name, not "Operations".

One partner's **production** record was entered as the company name. This is an internal SOP gap on our side as much as a partner-facing rule — nothing validates it at entry, so the failure surfaces later, in a report, against a record you can no longer casually change.

<Warning>
  **There is no self-service correction path for a production account or user record.**

  There is also **no re-KYC, reset or PII-update path** on the KYC surface. Treat the first entry as
  the only cheap entry: check the records before first traffic, not after your first report run.
</Warning>

What to check, per record, before production traffic:

* The account-level legal name is a natural person's full legal name.
* The user-level legal name matches that person's government identity, not a display name from your app.
* No record carries your firm's name, a team name, or a test string.
* Your own customer row stores the `participantId` we issued, verbatim, so the mapping back to the record is unambiguous.

### Address format

The one published constraint is narrow: **`postal_code` is five digits.** A nine-digit ZIP+4, a hyphenated form or a non-US postal code will not satisfy it.

<Warning>
  **The full address format requirement is not published.**&#x20;
  The five-digit `postal_code` constraint is the only address rule in the verified ledger, and it is
  not plausible that it is the only rule NFA reporting imposes. Ask for the field-by-field format
  before you build your address capture, and validate on entry rather than on submission.
</Warning>

Related field constraint on the same payload: **`external_id` is capped at 49 characters.**

<Note>
  There is also **no authoritative excluded-states or jurisdiction list** in our docs. One partner
  asked three times and was never answered.

  Do not infer eligibility from the address fields you collect.
</Note>

### `participantType` casing, per path

This is a live defect, and the IB path is the accidental beneficiary of it.

| Path                                                  | Vocabulary                        | Comparison                 | Consequence                                                             |
| ----------------------------------------------------- | --------------------------------- | -------------------------- | ----------------------------------------------------------------------- |
| **Create** — the gateway's REST admin-create endpoint | Spells ISV **uppercase**          | **Case-sensitive**         | The registry's canonical lowercase `isv` was **rejected with HTTP 400** |
| **Adopt**                                             | Normalizes with `strings.ToLower` | Case-insensitive in effect | Any casing is accepted and lowercased                                   |
| **Provision**                                         | Normalizes with `strings.ToLower` | Case-insensitive in effect | Any casing is accepted and lowercased                                   |

**`introducing_broker` is spelled identically in both vocabularies. The IB path has therefore only ever worked by orthographic accident** — not because the create path handles IB correctly, but because there is no casing difference to trip over.

Canonical casing to send, per path:

* **Create:** send the vocabulary spelling the create path validates against, exactly. For an IB that is `introducing_broker`; do not assume the uppercase transformation that ISV requires applies to you, and do not experiment on a production create.
* **Adopt and provision:** `introducing_broker`. Anything you send is lowercased, so this is the spelling that survives.

<Warning>
  **Which casing is canonical per endpoint is a defect being corrected, not a design.**

  Because the two vocabularies agree on `introducing_broker` today, a fix to either one could change
  your working call without any error you would predict. **Do not hard-code the type string in more
  than one place**, and if you get an HTTP 400 naming the participant type, report it verbatim rather
  than trying casings against production.
</Warning>

### Do not confuse the DCO clearing-member ID with the DCM firm name

You hold three identity strings, and two of them look interchangeable and are not.

| String                                               | What it is                                                            |
| ---------------------------------------------------- | --------------------------------------------------------------------- |
| Participant firm — `firms/…-api-participant`         | Holds your customers. Every `x-participant-id` must be scoped here    |
| Clearing-member firm — `firms/…-api-clearing-member` | The `firm_id` on your Auth0 token. Your account and clearing identity |
| **IB anchor DCO account**                            | The account-structure addition an ISV does not have                   |

**Confusing a DCO clearing-member ID with a DCM firm name has stalled an onboarding outright.** The failure is on our side, triggered by the record mismatch, and no error is returned to you — onboarding simply does not complete. Expect diagnosis to take time, so give us the identity strings up front.

Record all three strings verbatim at your credential gate, label them in your own configuration by what they are rather than by which page you got them from, and never construct one from another. `provisionedAccount`, the fully-qualified DCM account name, is separately **opaque: its account identifier cannot be derived from participant or user data.**

### Your recorded partner type

<Warning>
  **Get your firm's recorded partner type in writing.**&#x20;
  The taxonomy is unresolved internally and leaks to partners as mislabelled tickets and wrong account
  structures. **TSP is a fourth type in use internally** — one partner was labelled ISV and was
  actually a TSP. A wrong type produces a wrong account structure, and the account structure is what
  your reports are built on.
</Warning>

### What can go wrong with a record

| Symptom                                                      | Cause                                                                                    | What you do                                                                     |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| An NFA report errors on a record                             | The account or user legal name is not a physical person's name                           | Correct the record through your integration lead; there is no self-service path |
| A record cannot be corrected                                 | No re-KYC, reset or PII-update path exists                                               | Raise it as a go-live blocker with the `participantId` and the `external_id`    |
| `HTTP 400` naming the participant type                       | The create path compares case-sensitively against a vocabulary that spells ISV uppercase | Report the verbatim error. Do not sweep casings against production              |
| A KYC payload is rejected on the address                     | `postal_code` is not five digits                                                         | Validate five digits on entry. Ask for the rest of the format                   |
| `external_id` rejected                                       | Over the 49-character cap                                                                | Shorten it; keep your own longer key in your own system                         |
| Onboarding does not complete and no error is returned to you | A DCO clearing-member ID used where a DCM firm name belongs                              | Quote all three identity strings, labelled, in your first message               |
| Your tickets are routed as the wrong partner type            | The internal taxonomy is unresolved and TSP is also in use                               | Ask for your recorded type and pin the answer in your channel                   |

## Next

[Connect](/environments)
