Skip to main content

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 sign in on the web app (app.dev.predictstreet.sde.adifoundation.ai on testnet); that login is outside 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 lets anyone read your account data and take every action its scopes allow that needs no wallet signature, such as cancelling your orders. It cannot place orders or move money out of a vault without the wallet’s EIP-712 signature. Ask your integration manager to revoke it immediately.

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 the requirePerWalletKyc 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. 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. 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:
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.
    • 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:
      1. vault deploy (VaultFactory on-chain, ~10 sec to confirm);
      2. on-chain limits opening — DepositLimitRegistry.setTier(VIP) and setCustomCaps(0, 0, 0), plus setDepositsRestricted(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 tier NONE: every vault.depositERC20 would revert with KycRequired, and trading and withdrawals would be blocked on-chain.
      Until the vault is deployed, orders are rejected with vault_not_deployed. While the on-chain limits are still confirming, orders may also be rejected with kyc_blocked. Retry after a few seconds. Need a per-sub-account deposit cap? Contact your integration manager.
  • 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 the signer ↔ vault binding, so the X-User-Wallet header alone cannot authorise a trade for a wallet you don’t control.
  • Each X-User-Wallet is the order’s signer. Sign the EIP-712 order with that wallet’s private key and put its address in the struct’s signer field. The struct’s maker must be that wallet’s vault (VaultFactory.vaultOf(signer), returned as vaultAddress by GET /api/me/vault), with signatureType: 1 (VAULT). It is never the EOA itself, and signatureType: 0 is not accepted.
  • The REST body maker may be either the X-User-Wallet address or its vault. Any other value is rejected at the gateway with maker_mismatch (HTTP 401 on /api/orders/place, a per-entry REJECTED on /api/orders/place-batch).
  • If the signature does not recover to the X-User-Wallet address (for example, because it was signed with maker set to the EOA or with signatureType: 0), placement returns status: "REJECTED" with code: "bad_signature". details.platform_canonical_struct is 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:
  1. Partner name — internal label.
  2. Contact email — for rotation notices, incident pages, and SLA comms.
  3. Partner kind — single_wallet (default) or multi_wallet. Pick multi_wallet if one key needs to act across many wallets (trading desk with N sub-accounts). See Partner kinds above, including the market-maker requirement for orders:write.
  4. Associated wallet — required for single_wallet partners that need write scopes; the wallet must have signed in to the web app at least once and cleared KYC tier 1. For multi_wallet partners the REST API ignores it, but if it is set, /ws/user binds every connection to that wallet and ignores X-User-Wallet. Leave it empty if you need per-sub-account user streams.
  5. 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.
  6. 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. 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.

Public endpoints

No authentication required. Any rate limit on these endpoints is per source IP. An X-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}/ohlc
  • GET /api/events, /api/events/{slug}, /api/events/featured, /api/events/stats
  • GET /api/matches, /api/matches/{slug}
  • GET /api/tags
  • GET /api/platform/contracts, /api/platform/fee-config, /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).

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:
multi_wallet partner — add X-User-Wallet:
The REST maker field must equal the request’s effective wallet (the associatedWallet for single_wallet, or the X-User-Wallet header value for multi_wallet) or that wallet’s vault. Any other value is rejected with 401 maker_mismatch. This field does not change what you sign: the EIP-712 order is always verified with signer = the effective wallet, maker = its vault (VaultFactory.vaultOf(signer), returned by GET /api/me/vault) and signatureType: 1, and it must be signed with the effective wallet’s private key. The wallet needs a deployed vault first, otherwise orders are rejected with vault_not_deployed. See EIP-712 signing for the full struct.Your API key authenticates the HTTP request; the EIP-712 signature authorises the trade. Neither alone is sufficient. PredictStreet checks the signature when you place the order: a mismatch is rejected with bad_signature, and details.platform_canonical_struct shows the exact struct that was verified. CTFExchange checks it again on-chain before the trade settles.

What happens on every request

  1. Parse the X-Api-Key header into (env, keyId, secret). Bad shape → 401 api_key_bad_format.
  2. Lookup the key by keyId. Unknown → 401 api_key_unknown_key.
  3. Verify the hash in constant time. Mismatch → 401 api_key_bad_secret.
  4. 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.
  5. 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.
  6. Scope check. Missing → 403 api_key_scope_missing with the required scope in the response body.
  7. 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 associatedWallet for single_wallet, or the X-User-Wallet header value for multi_wallet). Same ceiling regardless of call volume distribution across your keys.
Every bucket that applies to an endpoint must pass. The first exhausted bucket returns 429, with 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’s associatedWallet 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.
Process:
  1. Issue the replacement key via admin. Both keys now work.
  2. Roll your services to the new key. No downtime.
  3. Revoke the old key via admin. Propagation is near-instant; 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 scheme for partner integrations. No cookies, no basic-auth fallback on core.api.dev.predictstreet.sde.adifoundation.ai. The API also accepts Authorization: Bearer tokens, but those are web-app user session tokens, not a partner credential. If both are sent, X-Api-Key wins. A request with neither on an authenticated endpoint gets 401 auth_required; a malformed key gets 401 api_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. CTFExchange verifies this cryptographically on-chain — no compromise of our infrastructure lets anyone move your funds without your private key.