Who this is for
Your backend calls the PredictStreet integrator API. Examples:
- Market-maker bot quoting a pre-approved wallet
- Copy-trading service mirroring strategy signals
- Data pipeline pulling your positions / fills into a warehouse
- Mobile backend that never ships secrets to the device
The integrator API (core.api.dev.predictstreet.sde.adifoundation.ai) is designed for
server-to-server traffic. Retail users don’t authenticate here —
they log into app.predictstreet.io with SIWE (off the scope of
this documentation).
Never ship an API key to a browser, mobile app bundle, public repo,
or any environment a user can inspect. A leaked key is equivalent
to a leaked seed phrase for your associated wallet — rotate
immediately via the admin panel.
X-Api-Key: ps_live_0123456789abcdef_AbCdEfGhIjKlMnOpQrStUvWxYz1234567
├─── env ───┤├── keyId ──┤├──────── secret ─────────┤
| Segment | What it is |
|---|
ps_live_ / ps_test_ | environment prefix — live is production / dev; test reserved for sandbox |
keyId | 16 hex chars. Public handle we look up — safe to put in logs, audit trails, dashboards |
secret | 32 bytes of entropy, base64url-encoded (43 chars). Never logged, never persisted in plaintext server-side |
Only a SHA-256 hash of secret (peppered with a server-side salt) is
stored. If our database leaks, your secret does not.
Partner kinds
Every partner is one of two kinds. The kind is set at partner creation
and decides where the request’s acting wallet comes from.
| Kind | When ops picks it | Acting wallet resolution |
|---|
single_wallet (default) | One bot, one strategy, one wallet — the legacy contract | partner.associatedWallet is fixed at creation; every request acts on that wallet |
multi_wallet | Trading firm with N sub-accounts, copy-trading platform, custodial broker — one key fans out across many wallets | Caller declares the actor on every request via the X-User-Wallet header |
Kind is stamped on the key when admin issues it. Switching kinds
post-issuance is supervised — operations refuses the flip if any active
key under the partner relies on the prior contract.
Compliance toggle on multi_wallet
Multi_wallet partners come in two compliance flavours, set by the
requirePerWalletKyc boolean on the partner record:
requirePerWalletKyc | Compliance contract | Use case |
|---|
true (default, safe) | Each X-User-Wallet sub-account must clear retail KYC via SumSub before the platform accepts orders/withdrawals on its behalf. KycGuard enforces tier ≥ 1 per request. | Sub-account models where each wallet is a distinct human (e.g. white-label retail brokers). |
false | The partner has been KYB-onboarded as a legal entity and takes compliance responsibility for every sub-account it dispatches. KycGuard skips. New sub-accounts are auto-onboarded on first request — core.users row created, vault deploy enqueued — without retail KYC. | Trading firms / market makers / submitters whose sub-accounts are operational addresses, not retail identities. |
Single_wallet partners always enforce KYC at API key creation time
(the partner’s associatedWallet must be APPROVED tier ≥ 1 before
any write-scope key issues), so the toggle is inert there.
single_wallet flow
Send the API key, nothing else. The associatedWallet on the partner
record is the request’s effective wallet for KYC, compliance, and
rate-limit buckets. Any X-User-Wallet header is ignored — the
legacy contract is preserved for SDKs that don’t know about the header.
If a single_wallet partner has no associatedWallet attached yet,
every authenticated endpoint rejects with 401
api_key_no_associated_wallet — admin must attach one before writes
or /me reads work.
multi_wallet flow
Send the API key plus X-User-Wallet: 0x… on every request:
curl -X POST https://core.api.dev.predictstreet.sde.adifoundation.ai/api/orders/place \
-H "X-Api-Key: ps_live_0123456789abcdef_AbCdEfGhIjKlMnOpQrStUvWxYz1234567" \
-H "X-User-Wallet: 0x1234567890AbCdEf1234567890aBcDeF12345678" \
-H "Content-Type: application/json" \
-d '{ "maker": "0x1234…", "signature": "0x…", … }'
The header value is lower-cased server-side and becomes the
request’s effective wallet for balances, positions, trades, fees,
rate-limit buckets, and audit trails — exactly as if a single_wallet
key were attached to that wallet.
Rules:
- Header missing → 401
api_key_user_wallet_required.
- Header malformed (not
0x + 40 hex) → 401 api_key_user_wallet_invalid.
- KYC enforcement on the header wallet is controlled by the partner’s
requirePerWalletKyc flag (see Partner kinds
table above). Two options:
requirePerWalletKyc: true (default) — every X-User-Wallet
sub-account must clear retail KYC (status APPROVED, tier ≥ 1)
via the same SumSub flow a retail user goes through. Used for
sub-account models where each underlying wallet is a distinct
person (Coinbase Prime style).
requirePerWalletKyc: false — the partner is KYB-onboarded as a
legal entity and takes compliance responsibility for its
sub-accounts. KycGuard skips. Used for trading firms / market
makers / submitters whose addresses are operational, not retail
identities. The first request through the key for a previously
unseen X-User-Wallet auto-enqueues a vault deploy; subsequent
requests use the deployed vault as soon as match-submitter
confirms.
- Each acting wallet still needs its own private key to sign the EIP-712
payload on writes. The on-chain
CTFExchange and Vault contracts
verify signer ↔ vault cryptographically — looser API-layer attribution
costs nothing structurally because the chain is the floor.
- The wallet you pass in
X-User-Wallet must match the maker field
inside the order payload (and the EIP-712 signer for that order).
Mismatches are caught at the matcher / on-chain layer with
bad_signature.
Getting a key
Keys are issued by PredictStreet ops via the admin panel, never
self-service. Contact your integration manager with:
- Partner name — internal label.
- Contact email — for rotation notices, incident pages, and
SLA comms.
- Partner kind —
single_wallet (default) or multi_wallet.
Pick multi_wallet if one key needs to act across many end-user
wallets (trading desk with N sub-accounts, copy-trading platform,
custodial broker). See Partner kinds above.
- Associated wallet — required for
single_wallet partners that
need write scopes; must have completed SIWE once and cleared
KYC tier 1. For multi_wallet partners this field is optional and
informational — KYC moves to the partner legal entity.
- IP allowlist (optional, recommended). Your egress IPs — if set,
any request from a different IP is rejected at the edge.
- Requested scopes — what endpoints you plan to hit (see table
below).
You will receive the plaintext key exactly once on creation. Copy
it into a secrets manager (AWS Secrets Manager, HashiCorp Vault,
Kubernetes Secret, 1Password) immediately. We cannot show it to you
again — if lost, we revoke and issue a new one.
Scopes
Each key carries a set of scopes. Requests are rejected with 403
api_key_scope_missing if the endpoint requires a scope the key
doesn’t carry.
| Scope | Grants |
|---|
markets:read | public market & orderbook reads (raises your rate-limit ceiling; unauthenticated callers can still hit these) |
events:read | public events reads |
matches:read | public matches reads |
portfolio:read | /api/me/balances, /api/me/positions, /api/me/trades, /api/me/fees, /api/me/fee-tier, /api/me/vault, /api/me/vault/emergency, /api/me/portfolio, /api/me/deposit-limits, /api/me/withdrawals (list / detail / fee / deposit-sources) |
orders:read | /api/orders/open, /api/orders/history, /api/orders/{id}, /api/orders/{id}/fills |
orders:write | /api/orders/place, /api/orders/cancel, /api/orders/cancel-all — for single_wallet partners requires associatedWallet with KYC tier ≥ 1; for multi_wallet partners KYC is at the partner level |
vault:write | /api/vault/split-signature, /api/vault/merge-signature, /api/withdrawals/request, /api/me/withdrawals/{id}/cancel — same KYC rule as orders:write |
Multiple scopes listed on an endpoint are all required (intersection,
not union). For most partners, orders:write + orders:read +
portfolio:read covers the day-one scope.
Endpoint coverage
Authenticated endpoints
Require X-Api-Key with the listed scope.
| Endpoint | Method | Scope |
|---|
/api/orders/place | POST | orders:write |
/api/orders/cancel | POST | orders:write |
/api/orders/cancel-all | POST | orders:write |
/api/orders/open | GET | orders:read |
/api/orders/history | GET | orders:read |
/api/orders/{id} | GET | orders:read |
/api/orders/{id}/fills | GET | orders:read |
/api/me/vault | GET | portfolio:read |
/api/me/vault/emergency | GET | portfolio:read |
/api/me/balances | GET | portfolio:read |
/api/me/positions | GET | portfolio:read |
/api/me/trades | GET | portfolio:read |
/api/me/fees | GET | portfolio:read |
/api/me/fee-tier | GET | portfolio:read |
/api/me/portfolio | GET | portfolio:read |
/api/vault/split-signature | POST | vault:write |
/api/vault/merge-signature | POST | vault:write |
/api/withdrawals/request | POST | vault:write |
/api/me/withdrawals | GET | portfolio:read |
/api/me/withdrawals/{id} | GET | portfolio:read |
/api/me/withdrawals/fee | GET | portfolio:read |
/api/me/withdrawals/deposit-sources | GET | portfolio:read |
/api/me/withdrawals/{id}/cancel | POST | vault:write |
/api/me/deposit-limits | GET | portfolio:read |
Public endpoints
No authentication required. Sending an X-Api-Key is still allowed
and raises your per-partner rate-limit ceiling:
GET /api/markets, /api/markets/{symbol}, /api/markets/{symbol}/orderbook, /api/markets/{symbol}/trades, /api/markets/{symbol}/ohlc
GET /api/events, /api/events/{id}
GET /api/matches, /api/matches/{id}
GET /api/tags
GET /api/platform/notices, /api/platform/status
GET /api/compliance/geo
GET /health, /ready
WebSocket
Both WebSocket gateways authenticate with the same X-Api-Key as
HTTP — send either the X-Api-Key header on the upgrade or a
?key=<token> query parameter (for browser clients that can’t set
headers on the WebSocket upgrade).
| Gateway | URL | Required scope |
|---|
/ws/user | wss://ws-gateway.dev.predictstreet.sde.adifoundation.ai/ws/user | portfolio:read (for user_activity) |
/ws/market | wss://ws-gateway.dev.predictstreet.sde.adifoundation.ai/ws/market | — (any active key) |
| WS channel | Gateway | Required scope |
|---|
user_activity | /ws/user | portfolio:read |
token_trades / token_book / condition_status / system | /ws/market | — |
Not available via API key
A small number of surfaces are intentionally not exposed to
integrators — they require retail-user SIWE identity and live on
app.predictstreet.io. As of the current release these include the
self-service profile / consent / KYC submission flows: partners
shouldn’t drive end-user identity capture from server-side because
SUMSUB and the geo-fingerprinting layer expect the live retail
session.
Withdrawals are now fully partner-driven via API key (request,
list, detail, cancel) — the vault-owner EIP-712 signature still gates
funds movement, so an API key alone cannot move money out of a vault.
See Withdrawals overview for the
dual-signature flow.
If you have a use case that needs something currently outside the
API-key scope, reach out — some can be unlocked with a stricter scope
set, others are architecturally out of scope.
Making a request
single_wallet partner:
curl -X POST https://core.api.dev.predictstreet.sde.adifoundation.ai/api/orders/place \
-H "X-Api-Key: ps_live_0123456789abcdef_AbCdEfGhIjKlMnOpQrStUvWxYz1234567" \
-H "Content-Type: application/json" \
-d '{
"marketId": "UAE-CUP-FINAL-20260425",
"side": "buy",
"outcome": "0",
"price": "0.55",
"quantity": "100",
"nonce": "1730289600000000",
"expiry": 1730376000,
"maker": "0xYOUR_ASSOCIATED_WALLET",
"signature": "0x…EIP-712 signature over the order…"
}'
multi_wallet partner — add X-User-Wallet:
curl -X POST https://core.api.dev.predictstreet.sde.adifoundation.ai/api/orders/place \
-H "X-Api-Key: ps_live_0123456789abcdef_AbCdEfGhIjKlMnOpQrStUvWxYz1234567" \
-H "X-User-Wallet: 0x1234567890AbCdEf1234567890aBcDeF12345678" \
-H "Content-Type: application/json" \
-d '{
"marketId": "UAE-CUP-FINAL-20260425",
"side": "buy",
"outcome": "0",
"price": "0.55",
"quantity": "100",
"nonce": "1730289600000000",
"expiry": 1730376000,
"maker": "0x1234567890AbCdEf1234567890aBcDeF12345678",
"signature": "0x…EIP-712 signed by that maker's key…"
}'
maker must equal the request’s effective wallet (the
associatedWallet for single_wallet, or the X-User-Wallet header
value for multi_wallet) or its VaultFactory-derived vault, and the
EIP-712 signature must be signed by that wallet’s private key. Your
API key authenticates the HTTP request; the EIP-712 signature
authorises the on-chain trade. Neither alone is sufficient — on-chain
CTFExchange verifies the signature cryptographically before the tx
settles.
What happens on every request
- Parse the
X-Api-Key header into (env, keyId, secret). Bad
shape → 401 api_key_bad_format.
- Lookup the key by
keyId (Redis cache, 60s TTL; falls through
to Postgres on miss). Unknown → 401 api_key_unknown_key.
- Verify the hash in constant time. Mismatch → 401
api_key_bad_secret.
- Lifecycle checks: revoked → 401
api_key_revoked; expired →
401 api_key_expired; partner suspended → 401
api_key_suspended; source IP not in allowlist (if set) → 401
api_key_ip_denied.
- Scope check. Missing → 403
api_key_scope_missing with the
required scope in the response body.
- Resolve effective wallet — depending on partner kind:
single_wallet → partner.associatedWallet. Not attached → 401
api_key_no_associated_wallet.
multi_wallet → the X-User-Wallet header (lower-cased). Header
missing → 401 api_key_user_wallet_required; not a 0x-prefixed
40-hex address → 401 api_key_user_wallet_invalid.
- Set identity — the resolved wallet becomes the request’s
effective wallet for all downstream checks (KYC, compliance,
rate-limit buckets, audit trails).
Rate limits
Every API-key request passes through two independent buckets:
- IP bucket — shared edge defence (same as anonymous traffic).
- Wallet bucket — per effective wallet (the
associatedWallet for
single_wallet, or the X-User-Wallet header value for
multi_wallet). Same ceiling regardless of call volume distribution
across your keys.
Both must pass. Response headers expose the tightest applicable
bucket’s state (X-RateLimit-Limit, -Remaining, -Reset). On
429, Retry-After is set to the bucket-reset delta.
Per-partner organisation-wide bucket exists in the partner record
(rateLimitPerMin) but is not currently enforced at the request
boundary. Contact your integration manager if you need a
partner-wide ceiling — operations can lower the per-wallet cap as
a stop-gap.
Geo handling
Geographic blocking (FATF blacklist / greylist) does not apply to
API-key requests — integrator egress IPs are data-centre / VPN
class and blocking them isn’t a meaningful control (you could proxy
trivially). Compliance stays in force at the wallet level via
KYC / AML / RG gates on the effective wallet — for single_wallet
that’s the partner’s associatedWallet, for multi_wallet that’s
the partner legal entity that took on the regulated counterparty
obligation.
Rotation
Rotate keys pre-emptively on any of:
- Employee who had access leaves.
- Secrets-manager misconfiguration suspected.
- Source-code leak.
- Routine 6-month rotation hygiene.
Process:
- Issue the replacement key via admin. Both keys now work.
- Roll your services to the new key. No downtime.
- Revoke the old key via admin. Propagation is near-instant
(Redis pub-sub invalidation); worst-case lag 60s.
A revoked key rejects every subsequent request with 401
api_key_revoked. Re-enabling is not supported — issue a new key.
Security invariants
- X-Api-Key is the only session scheme. No cookies, no bearer
tokens, no basic-auth fallback on
core.api.dev.predictstreet.sde.adifoundation.ai. Missing
or malformed header on an authenticated endpoint → 401.
- Scopes are enforced server-side. Adding a scope to your key’s
stored list is the only way to access a gated endpoint —
client-side claims are ignored.
- Writing still requires EIP-712. The API key proves you’re a
pre-approved partner; the EIP-712 order signature proves you
authorised the specific trade.
CTFExchange verifies this
cryptographically on-chain — no compromise of our infrastructure
lets anyone move your funds without your private key.
- HMAC request signing (adding an
X-Api-Signature header over
timestamp + method + path + body) is on the roadmap as an
optional hardening for production partners; not required today.