Getting started

Environments

Sandbox is a property of the tenant, not of a request or a host name. Read simulated on every response rather than inferring the environment from the URL you happen to be pointed at.

What is real in this build

Catalogue livePrices liveExecution simulatedCustomer custody noneWebhook delivery off
LayerStateWhat that means for you
Market catalogueLiveReal questions, real outcomes, normalised and de-duplicated across sources.
PricesLiveStreamed or polled from upstream books. Age is published on every quote.
QuotesLive pricing, real expiryPriced from a real book where one exists, and the 30-second expiry is enforced against the stored timestamp.
Orders, fills, positionsRecordedReal idempotency, real state machine, a locally produced fill. Written to the same tables in the same shape a live venue will write them.
SettlementRealPositions settle against their contract’s resolution and the credit is computed once, on our side.
Money movementYoursPredicta returns exact debit, release and credit instructions. Your ledger applies them.
Webhook deliveryQueued, not sentEvents are durable rows. The sender is switched off: poll instead.

Who holds the money

This is the fact that decides how your integration is shaped, and it does not change between sandbox and live: the operator owns its customers' cash ledger. Predicta never holds player funds. What Predicta returns is an exact instruction against your own ledger — authorizationAmount to reserve, actualDebit to take, releaseAmount to give back, sellProceeds to credit on an exit, settlementCredit or voidCredit when a position resolves.

Those amounts are exact decimal strings on the wire ("25.00"), never JSON numbers. The float fields beside them — stake, notional, predictaFee, averagePrice — are for display.

Three endpoint groups are sandbox-only

Sandbox has simulated money, because somebody learning the flow needs funds to spend and somewhere to watch them move. Test funds arrive as a SANDBOX_CREDIT movement from house equity, and the balance and movement reads report it back.

Endpoint groupIn live mode
GET /api/v1/users/{id}/balance403 sandbox_only
GET /api/v1/users/{id}/ledger403 sandbox_only
/api/v1/funding/*403 sandbox_only

A Predicta-held balance is not the production integration path

These endpoints model a per-user cash position derived from Predicta's own ledger. That is real and useful in the sandbox and it is the wrong model for a live operator: a partner reading a balance from here would be treating Predicta as a custodian of its players' funds, which Predicta is not, and would eventually reconcile its own books against a number that describes something else.

So they refuse in live mode rather than returning a figure that invites the misreading. Build against the instructions on quotes, orders and settlements, and your own wallet stays the authority on what a player may stake.

The simulated flag

Every quote, order and settlement payload carries simulated. It is present on every response and never omitted when inconvenient, because a simulated fill that looks identical to a real one is exactly what an integrator should not have to read the docs to discover.

json
"simulated": true    // the fill was produced locally; no order reached a venue

Read this

simulated: true is the answer on every response in this build. If your code treats a simulated fill and a live fill the same way, it will keep working when execution is switched on — and if it does not, you find out here rather than in production.

Routing, stated precisely

Predicta groups questions across sources by exact match. That grouping is a display fact: it says two listings ask the same question, not that the contracts are interchangeable. Cutoff times, resolution sources and edge cases in the rules all differ.

  • An order is bound to the contract that was quoted. The quote reports routing.consideredVenues — how many sources could have competed — and routing.routable, whether any actually may.
  • The alternatives themselves are never listed, because naming them is naming the venues. No partner-facing payload identifies a source anywhere.
  • routable is false unless a human has verified a group as equivalent, which is the answer for every group today.
  • Predicta does not claim best execution across sources, and will not until that verification exists.

Sandbox and production hosts

There is one deployment. A key does not encode a host, and there is no separate sandbox domain to point at: your integration host is issued to you directly. Configure it as a variable rather than hard-coding it, so a different host can be swapped in without a code change.

shell
PREDICTA_BASE=https://<issued-to-you>
PREDICTA_KEY=pk_test_...        # sandbox tenant. Live keys are sk_live_...

Sandbox is a property of the tenant

Not of a request, not of a header, not of the URL. The operator record carries the mode and every request inherits it, so there is no parameter a caller could set to reach the other environment and no way to place a live order with a test key by accident.

The sandbox is not a lesser environment

It runs the production pipeline: live prices, real quote expiry, real idempotency, real settlement, the same tables and the same code. What differs is that the fill is produced locally instead of at a venue, and that the money endpoints above are open. Everything you prove there about idempotency, recovery and settlement transfers unchanged.

Health and status

GET /api/health is unauthenticated and reports whether the app can serve, how fresh each price feed is, and how much of the catalogue is streamed rather than polled.

200 application/json (abridged)
{
  "status": "ok",                  // ok | degraded | starting | unhealthy
  "database": "up",
  "markets": { "total": 22936, "open": 22936, "resolved": 0 },
  "stream": { "running": true, "subscriptions": 1500, "openContracts": 22936,
              "streamPricedContracts": 1489, "restPricedContracts": 21447 },
  "pricing": { "houseEdgeBps": 0, "maxUsableSpread": 0.2 },
  "timestamp": "2026-08-20T04:43:02.980Z"
}
  • degraded is a 200. A single upstream feed being unhealthy is an operating state, not an outage, and your monitoring should be able to tell the two apart.
  • Only a dead database returns 503.
  • A feed that has not synced in several reconcile intervals reports stale even if its last sync succeeded: liveness, not last-known-status.