Lifecycle

Settlement

A settlement record is an instruction: this position closed, this is what to credit. It is computed once, on Predicta’s side, so that two systems are never doing the same arithmetic.

The settlement register

GET/api/v1/settlementsAPI key
What settled and what it paid. Filterable, keyset-paged, safe to re-read. settlements:read.
ParameterNotes
externalUserIdYour own id for the customer. An id that has never traded is 404 unknown_user, not an empty page.
outcomewon, lost or void.
settledFrom / settledToISO-8601, inclusive.
limit1–500, default 100.
cursorOpaque. Echo nextCursor from the previous page.
shell
curl -s "$PREDICTA_BASE/api/v1/settlements?externalUserId=user-4471&outcome=won&limit=100" \
  -H "Authorization: Bearer $PREDICTA_KEY"
200 application/json
{
  "settlements": [
    {
      "id": "stl_…",                  // stable. Safe as the idempotency key for a credit.
      "positionId": "pos_…",
      "externalUserId": "user-4471",  // YOUR id, never a Predicta uuid
      "side": "YES",
      "resolution": "YES",            // YES | NO | VOID, as resolved upstream
      "outcome": "won",               // won | lost | void, for this position
      "contracts": 68.6111,
      "realisedPnl": 18.61,           // THIS settlement's
      "lifetimeRealisedPnl": 18.61,   // the position's running total
      "settledAt": "2026-08-21T18:02:11.004Z",
      "operatorMoney": {
        "currency": "USD",
        "settlementCredit": "68.61",  // CREDIT THIS
        "voidCredit": "0.00"
      },
      "cursor": "…"
    }
  ],
  "nextCursor": "…",
  "hasMore": false
}

realisedPnl is this settlement’s, not the position’s lifetime

The two differ whenever a position was partly exited before it resolved. Reporting the lifetime figure under a per-settlement name overstates the period, so both are published and each is named for what it is. Sum realisedPnl for a period; lifetimeRealisedPnl is the position's running total.

Exactly one credit is non-zero

outcomesettlementCreditvoidCredit
won$1.00 × contracts, rounded down to the cent.0.00
lost0.000.00 — a loss zeroes both. Nothing to credit.
void0.00Everything the player committed, including the platform fee.

A void refunds the commission as well as the stake. Predicta did not provide the market, so it does not keep the fee: quietly retaining it would be a new commercial term introduced by a backend decision. The two fields are separate rather than one signed amount because a refund and a win are different postings in an operator's books.

A contract settles at exactly $1.00 or exactly $0.00. There is no third case: a market that resolved 99% certain did not resolve.

Consuming it safely

  • Key your credit on the settlement's id — it is stable and it is what makes re-processing a no-op. positionId works equally well and is what the webhook keys on, so a partner using both never processes the same fact twice.
  • Advance your stored cursor only after the batch is applied. Reading does not consume: the same cursor returns the same page, so a consumer that crashes mid-batch re-reads rather than skips.
  • The register is scoped to your operator inside the query, not filtered afterwards. Another partner's settlements are not reachable by guessing a cursor.
  • hasMore: false means caught up. Poll on the order of once a minute; settlements are not high-frequency events.
typescript
for await (const s of predicta.iterateSettlements({ settledFrom: lastRun })) {
  // s.operatorMoney.settlementCredit and .voidCredit are exact Decimal strings.
  await ledger.creditOnce(s.id, s.externalUserId, s.operatorMoney);
}

The older feed

GET/api/v1/settlements/positionsAPI key
Deprecated. Its response carries supersededBy: "/api/v1/settlements".

It pages on settledAt with no tiebreaker — and settlements written in one transaction share a timestamp exactly — so it can drop or repeat rows at a page boundary. It is left working rather than removed because it is already a published contract, but new integrations should use the register above: same records, keyset cursor on a monotonic sequence, no gap.

Why you cannot be paid twice

Three layers, each of which would be sufficient alone:

  • The position row is locked for update inside the settlement transaction, so two concurrent sweeps serialise rather than both reading it as open.
  • Only a position still marked open is considered.
  • The settlement insert carries a unique index on positionId. If it inserts nothing, no credit is computed, no status is written and no event is emitted.

The third is the one that holds if the others are ever wrong, because it is a database constraint rather than a branch in application code.

Voids and amendments

Upstream sources sometimes change a published result. Predicta handles that by refusing to pretend it did not happen, and by refusing to act unilaterally.

SituationWhat Predicta does
A contract resolves normallyPositions settle against that contract’s own resolution, and no other. A grouped listing elsewhere settles independently and is permitted to disagree.
A contract closes with no published resultIt is reported as determined, never guessed. Nothing settles.
A resolution changes after positions settledThe contract moves to needs_review, which stops anything further from settling against either value, and a market.resolution_changed event names the affected positions.

An amendment is a notification, not an instruction

Predicta never reverses a settlement, adjusts a figure or rewrites a settlement row. Money that left your ledger on the strength of a published outcome is not clawed back by a background job: the source may be correcting a mistake, or making one. You receive the exact list of affected positions and the decision is yours.

Proving the period balances

The register answers “what settled”. To check that every money fact of a period agrees — trial balance, cash statement, venue agreement, and the individual findings when it does not — read Reconciliation. Its feed carries position.settled alongside every fill, fee and collateral movement, totally ordered and resumable, which is the stream to build a nightly close on.

The public market feed

GET /api/settlements is unauthenticated and publishes market outcomes: one row per resolved contract, with no reference to who held what. It is the right feed only if you run your own book against Predicta's resolutions. If Predicta recorded the positions, use the register above — working out who held what from a market outcome is precisely the arithmetic that must not be performed in two places.