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
core.api.dev.predictstreet.sde.adifoundation.ai) is designed for
server-to-server traffic. Retail users sign in on the web app
(app.dev.predictstreet.sde.adifoundation.ai on testnet); that login
is outside the scope of this documentation.
Format
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 belongs to the partner, not to individual keys, so every key
under a partner behaves the same way. Operations can change a
partner’s kind, its
requirePerWalletKyc setting or its associated
wallet only after all of its active orders:write / vault:write
keys are revoked.
A multi_wallet key with orders:write is issued only to a partner
registered as a market maker with at least one active registered
trading wallet.
Compliance toggle on multi_wallet
Multi_wallet partners come in two compliance flavours, set by therequirePerWalletKyc boolean on the partner record:
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. TheassociatedWallet on the partner
record is the request’s effective wallet for KYC, compliance, and
rate-limit buckets. Any X-User-Wallet header is ignored.
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 plusX-User-Wallet: 0x… on every request:
- Header missing → 401
api_key_user_wallet_required. - Header malformed (not
0x+ 40 hex) → 401api_key_user_wallet_invalid. - KYC enforcement on the header wallet is controlled by the partner’s
requirePerWalletKycflag (see Partner kinds table above). Two options:-
requirePerWalletKyc: true(default) — every X-User-Wallet sub-account must clear retail KYC (statusAPPROVED, 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. -
requirePerWalletKyc: false— the partner is KYB-onboarded as a legal entity and takes compliance responsibility for its sub-accounts. Per-wallet KYC is not checked. 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:- vault deploy (
VaultFactoryon-chain, ~10 sec to confirm); - on-chain limits opening —
DepositLimitRegistry.setTier(VIP)andsetCustomCaps(0, 0, 0), plussetDepositsRestricted(false)/setTradingRestricted(false)/setWithdrawalsRestricted(false).setCustomCaps(0, 0, 0)clears any per-wallet override, so the VIP tier applies: no on-chain deposit ceiling on singleDeposit / 30d / 12m. PredictStreet broadcasts all five ops; the sub-account is fully usable end-to-end (deposit + trade + withdraw) within ~30 sec of the first request. Without this auto-step the wallet would stay at tierNONE: everyvault.depositERC20would revert withKycRequired, and trading and withdrawals would be blocked on-chain.
vault_not_deployed. While the on-chain limits are still confirming, orders may also be rejected withkyc_blocked. Retry after a few seconds. Need a per-sub-account deposit cap? Contact your integration manager. - vault deploy (
-
- Each acting wallet still needs its own private key to sign the EIP-712
payload on writes. The on-chain
CTFExchangeandVaultcontracts verify the signer ↔ vault binding, so theX-User-Walletheader alone cannot authorise a trade for a wallet you don’t control. - Each
X-User-Walletis the order’s signer. Sign the EIP-712 order with that wallet’s private key and put its address in the struct’ssignerfield. The struct’smakermust be that wallet’s vault (VaultFactory.vaultOf(signer), returned asvaultAddressbyGET /api/me/vault), withsignatureType: 1(VAULT). It is never the EOA itself, andsignatureType: 0is not accepted. - The REST body
makermay be either theX-User-Walletaddress or its vault. Any other value is rejected at the gateway withmaker_mismatch(HTTP 401 on/api/orders/place, a per-entryREJECTEDon/api/orders/place-batch). - If the signature does not recover to the
X-User-Walletaddress (for example, because it was signed withmakerset to the EOA or withsignatureType: 0), placement returnsstatus: "REJECTED"withcode: "bad_signature".details.platform_canonical_structis the exact struct the platform verified. Compare it field by field with the struct you signed.
Getting a key
Partner 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) ormulti_wallet. Pickmulti_walletif one key needs to act across many wallets (trading desk with N sub-accounts). See Partner kinds above, including the market-maker requirement fororders:write. - Associated wallet — required for
single_walletpartners that need write scopes; the wallet must have signed in to the web app at least once and cleared KYC tier 1. Formulti_walletpartners the REST API ignores it, but if it is set,/ws/userbinds every connection to that wallet and ignoresX-User-Wallet. Leave it empty if you need per-sub-account user streams. - IP allowlist — not offered at the moment. Do not request one:
the platform cannot yet match your egress IP reliably behind its
edge, so an allowlisted key would be refused on every call
(
api_key_ip_denied). Protect the key by keeping the secret server-side and requesting only the scopes you need. - Requested scopes — what endpoints you plan to hit (see table below).
Scopes
Each key carries a set of scopes. Requests are rejected with 403api_key_scope_missing if the endpoint requires a scope the key
doesn’t carry.
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
RequireX-Api-Key with the listed scope.
Public endpoints
No authentication required. Any rate limit on these endpoints is per source IP. AnX-Api-Key header is ignored here and does not raise it:
GET /api/markets,/api/markets/{slug},/api/markets/{symbol}/orderbook,/api/markets/{symbol}/trades,/api/markets/{symbol}/ohlcGET /api/events,/api/events/{slug},/api/events/featured,/api/events/statsGET /api/matches,/api/matches/{slug}GET /api/tagsGET /api/platform/contracts,/api/platform/fee-config,/api/platform/notices,/api/platform/statusGET /api/compliance/geoGET /health,/ready
WebSocket
Both WebSocket gateways authenticate with the sameX-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).
Not available via API key
A small number of surfaces are intentionally not exposed to integrators — they require a signed-in web-app user session and live on the web app (app.dev.predictstreet.sde.adifoundation.ai on testnet).
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 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:X-User-Wallet:
What happens on every request
- Parse the
X-Api-Keyheader into(env, keyId, secret). Bad shape → 401api_key_bad_format. - Lookup the key by
keyId. Unknown → 401api_key_unknown_key. - Verify the hash in constant time. Mismatch → 401
api_key_bad_secret. - Lifecycle checks: revoked → 401
api_key_revoked; expired → 401api_key_expired; partner suspended → 401api_key_suspended; source IP not in allowlist (if set) → 401api_key_ip_denied. - Resolve effective wallet — depending on partner kind:
single_wallet→partner.associatedWallet. Not attached → 401api_key_no_associated_wallet.multi_wallet→ theX-User-Walletheader (lower-cased). Header missing → 401api_key_user_wallet_required; not a 0x-prefixed 40-hex address → 401api_key_user_wallet_invalid.
- Scope check. Missing → 403
api_key_scope_missingwith the required scope in the response body. - Set identity — the resolved wallet becomes the request’s effective wallet for all downstream checks (KYC, compliance, rate-limit buckets, audit trails).
Rate limits
API-key requests are counted in two kinds of independent bucket:- IP bucket — shared edge defence (same as anonymous traffic).
- Wallet bucket — per effective wallet (the
associatedWalletfor single_wallet, or theX-User-Walletheader value for multi_wallet). Same ceiling regardless of call volume distribution across your keys.
Retry-After set to that bucket’s
wait. The X-RateLimit-Limit / -Remaining / -Reset headers
describe the endpoint’s own bucket (the wallet bucket where one
applies), not necessarily the tightest one.
Your partner record’s rate tier (rateLimitPerMin, default 600)
scales every wallet bucket your keys hit, and the per-partner buckets
of the partner trade endpoints:
effective limit =
floor(listed limit × rateLimitPerMin / 600), minimum 1. 600
applies the published limits as-is and 1200 doubles them. IP
buckets are never scaled. To change your tier, contact your
integration manager. See Rate limits for the
per-endpoint table.
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: the partner’sassociatedWallet for single_wallet, or the X-User-Wallet
sub-account for multi_wallet. Per-wallet KYC is waived only for
multi_wallet partners with requirePerWalletKyc: false, where the
KYB-onboarded partner takes that responsibility.
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.
- 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; worst-case lag 60s.
api_key_revoked. Re-enabling is not supported — issue a new key.
Security invariants
- X-Api-Key is the only scheme for partner integrations. No
cookies, no basic-auth fallback on
core.api.dev.predictstreet.sde.adifoundation.ai. The API also acceptsAuthorization: Bearertokens, but those are web-app user session tokens, not a partner credential. If both are sent,X-Api-Keywins. A request with neither on an authenticated endpoint gets 401auth_required; a malformed key gets 401api_key_bad_format. - 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.
CTFExchangeverifies this cryptographically on-chain — no compromise of our infrastructure lets anyone move your funds without your private key.