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

# API Reference

> Complete catalogue of PredictStreet's integrator HTTP endpoints — request / response schemas, example payloads, and a try-it-out panel for supported environments.

PredictStreet's integrator API is **server-to-server only**. Every
authenticated endpoint takes a single header — **`X-Api-Key`** —
backed by a per-partner secret your ops contact hands you at
onboarding. There is no OAuth, no SIWE, no cookie session. Public
read endpoints (markets / events / matches / tags) work without a
key but accept one to raise your rate-limit ceiling.

## First time here?

<Steps>
  <Step title="Get an API key">
    Keys are issued by PredictStreet ops, never self-service. Email
    your integration manager (or
    [partners@predictstreet.com](mailto:partners@predictstreet.com))
    with the four items in
    [API keys → Getting a key](/auth/api-keys#getting-a-key):
    partner name, contact email, associated wallet (the EOA you'll
    sign EIP-712 orders with), and the scopes you need.
  </Step>

  <Step title="Walk through the testnet quickstart">
    [Quickstart](/quickstart) takes the private key of your
    `associatedWallet` from zero to a settled trade in five steps —
    mint USDC, deploy/verify your vault, deposit, sign + place an
    order, observe settlement. Every code block is real ethers v6
    against the live testnet; copy-paste, swap your secrets, run.
  </Step>

  <Step title="Mental model — Wallet, vault, scopes">
    Every API key is bound at issue time to one (or many) **associated
    wallets** — the EOAs whose private key you hold and which sign
    EIP-712 orders. Each associated wallet has a deterministic
    **vault** contract that holds your USDC and your outcome shares;
    the vault is the `maker` field of every signed Order. Vaults
    auto-deploy after the wallet clears KYC tier 1 — you never call
    `VaultFactory.deployVault()` manually unless you want to. **Scopes**
    on the key gate which endpoint groups the key can hit
    (`orders:write`, `portfolio:read`, `vault:write`, public reads).
    The full mental model lives at
    [Platform overview](/platform-overview); the concept pages on
    [Vaults](/concepts/vaults/overview) and
    [Deposits](/concepts/deposits/overview) round it out.
  </Step>
</Steps>

## Authentication on the playground

Every locked endpoint shows a lock icon with `ApiKeyAuth`. To enable
Try-it-out:

1. Click **Authorize**.
2. Paste the full key (e.g. `ps_live_0123456789abcdef_AbCdEfGhIjKlMnOpQrStUvWxYz1234567`)
   into the **X-Api-Key** field.
3. The key persists across endpoint pages in your browser session.

Scopes are enforced server-side — your key must carry the scope
listed on the operation (`orders:write`, `portfolio:read`, etc.) or
the request returns 403 `api_key_scope_missing`.

For multi-wallet partners (one key dispatching N actor wallets), set
the **`X-User-Wallet`** header on every request to the actor wallet
the call should run as. Single-wallet partners ignore that header —
the key's static `associatedWallet` is used unconditionally. See
[Partner kinds](/auth/api-keys#partner-kinds)
for the full distinction.

See [API keys](/auth/api-keys) for format, full scope taxonomy,
endpoint coverage table, rate-limit buckets, and rotation
procedure.

## Endpoint groups

Use the left sidebar to browse. The **Scope** column lists the API-key
scope each group requires — write scopes (`orders:write`,
`vault:write`) are issued only to keys whose `associatedWallet` has
cleared KYC tier 1.

| Group             | Required scope                                   | What's in there                                                         |
| ----------------- | ------------------------------------------------ | ----------------------------------------------------------------------- |
| **Events / Tags** | *(public)*                                       | Event discovery and curated tag taxonomy                                |
| **Matches**       | *(public)*                                       | Fixture / card grouping multiple events into one matchup                |
| **Markets**       | *(public)*                                       | List markets, detail, orderbook, public trades, OHLC                    |
| **Orders**        | `orders:read` (GET) / `orders:write` (POST)      | Place / cancel / read signed orders                                     |
| **Portfolio**     | `portfolio:read`                                 | Balances, positions, trades, fees, fee tier, vault info, deposit limits |
| **Vault**         | `vault:write`                                    | Dual-sig signature requests for `splitPosition` / `mergePositions`      |
| **Withdrawals**   | `vault:write` (mutate) / `portfolio:read` (read) | Request, list, fee preview, deposit-source pin, cancel                  |

Surfaces that require retail-user SIWE identity (user profile, KYC
submission, consent management, responsible-gambling, session
management) live on the retail web surface — `app.predictstreet.io`
— and are intentionally **not** exposed to integrators. The
SUMSUB / geo-fingerprinting layer expects a live retail session for
those flows. Partners who need a use case currently outside the
API-key surface should reach out — some can be unlocked with a
stricter scope set, others are architecturally out of scope.

## Conventions

* **Base URL:** `https://core.api.dev.predictstreet.sde.adifoundation.ai` (testnet).
  Staging and mainnet URLs delivered at partner onboarding.
* **Numeric fields** are encoded as strings to preserve precision
  (e.g. `"price": "0.5214"`, not `0.5214`). Deserialize into
  `decimal.Decimal` / `BigDecimal` / `ethers.parseUnits` — not
  `Number`.
* **Timestamps** are ISO-8601 UTC (e.g. `"2026-04-22T10:31:05.124Z"`).
* **Errors** share a common envelope: `{ code, message, details? }`.
  See [Error codes](/errors/codes) for the full catalogue.
* **Rate limits** — three buckets (IP / wallet / partner) apply to
  every authenticated request; see
  [Rate limits](/auth/rate-limits).
* **Business rejects** on some endpoints return HTTP `200` (or
  `201` for POSTs) with a `status: "REJECTED"` + `code` field
  inside the response envelope — notably `POST /api/orders/place`,
  which returns `201 Created` even when the matcher rejects the
  order. Always read the body's `status` field, not just the HTTP
  code. Full catalogue in [Errors overview](/errors/overview).
* **Enum query params** (e.g. `?status=` on `/api/orders/history`,
  `?interval=` on `/api/markets/{symbol}/ohlc`) reject unknown
  values with `400 bad_request`. Typos like `?status=CANCELED`
  (one L) used to silently return an empty result; they now fail
  fast.
* **Unknown query params** that aren't in the enum-validated set
  (`cursor`, extra filters core-api doesn't know about) are
  silently ignored rather than rejected. Don't rely on this
  behaviour — future versions may tighten to 400.
* **Address case** — Both transports return addresses in
  **lowercase** (`0xa1b2…`). HTTP responses (`walletAddress`,
  `vaultAddress`, etc.) and WebSocket payloads agree on the same
  canonical form, so cross-transport equality works without
  normalisation. Bodies you SEND can use either case — the
  server-side index is `LOWER(address)`. EIP-55 checksum-case input
  is accepted but never echoed back; expect lowercase on every
  response.
* **Order id field** — created via `POST /api/orders/place` returns
  the identifier under `orderId`; the same value surfaces under
  `id` on `GET /api/orders/{id}`, `GET /api/orders/open`, and
  `GET /api/orders/history`. The cancel request body uses
  `orderId` to match the placement response. Treat both names as
  the same value — the variance is request-vs-response convention,
  not a different identifier.
* **Standard response headers** — every response carries these
  beyond the rate-limit triple already documented in
  [Rate limits](/auth/rate-limits):
  * `X-API-Version` — additive version tag (`2026-04-01` today).
    Breaking changes go to `/api/v2/*`; the header lets a client
    pin behaviour without parsing URLs.
  * `X-Server-Time` — ms epoch wall-clock at response emission.
    Use as an authoritative ordering anchor between events arriving
    on the two transports. The WebSocket companion is the
    `serverTs` field stamped on every outbound frame; both clocks
    come from the same NTP-synced backend pool, so a REST
    `X-Server-Time` and a near-simultaneous WS `serverTs` are
    directly comparable.
  * `X-Request-Id` — unique per-request identifier echoed in audit
    logs. Include it when filing a support ticket so ops can
    correlate end-to-end.
    All three are in the CORS `Access-Control-Expose-Headers`
    allowlist so browser clients can read them on cross-origin
    responses.

## Where to go next

<CardGroup cols={2}>
  <Card title="API keys — format, scopes, rotation" icon="key" href="/auth/api-keys">
    The canonical reference for the auth header, scope taxonomy, IP
    allowlisting, multi\_wallet vs single\_wallet kinds, and key
    rotation procedure.
  </Card>

  <Card title="Quickstart — testnet end-to-end" icon="rocket" href="/quickstart">
    Five-step copy-paste from API key to settled trade against the
    live testnet.
  </Card>

  <Card title="Vaults — what they are + how they deploy" icon="vault" href="/concepts/vaults/overview">
    Why every partner has a vault, how auto-deploy works, what
    `Order.maker` should be, and when you'd ever deploy manually.
  </Card>

  <Card title="EIP-712 signing — Order struct + domain" icon="file-signature" href="/concepts/trading/eip712-signing">
    The 11-field `Order` struct, the binary vs neg-risk
    `verifyingContract` swap, and end-to-end signing examples.
  </Card>
</CardGroup>
