Direct Linking Integration with the ProphetX Trading API
Audience: engineering teams at order aggregators integrating with ProphetX
Overview
This guide walks an order aggregator through linking a user's ProphetX account on their own web/app, then placing orders directly from their web/app using that user's own ProphetX universal Trading API key/secret. The aggregator's backend runs a separate integration, under the aggregator's own ProphetX account, purely to ingest market fixture data and real-time order book updates — it never holds or uses an individual user's key/secret.
High-level steps:
- Link the user's ProphetX account from the client (login + 2FA).
- Generate the user's ProphetX trading API key/secret and store it on the user's device only.
- Ingest event/market fixture data server-side, using the aggregator's own ProphetX account's Trading API key/secret.
- Connect the aggregator's backend to ProphetX's WebSocket for real-time order book data, using the aggregator's own account.
- Run a geolocation check before every order.
- Submit the order directly from the client using the user's own key/secret; the client also listens on its own WebSocket channel for that order's status.
- Certify with ProphetX before going live.
Before you start
- The Universal Trading API is not yet open for public self-serve signup. The aggregator needs its own ProphetX API/market-maker account (separate from any end user's account) to run Steps 3–4. Contact ProphetX's market-making team to get set up in sandbox and, later, production.
- Two different credential types are in play here and should not be confused: the aggregator's own access key/secret (server-side, used only for fixture ingestion and market-wide order book data in Steps 3–4) and each individual user's access key/secret (client-side only, generated in Step 2, used only by that user's own client in Step 6).
- Sandbox market-maker endpoints in Steps 3–4 are relative to
https://api.sandbox.prophetx.dev/partner. Keep the/partnerpath as-is.
Step 1: Link the user's ProphetX account
When the user clicks "Link ProphetX" in the aggregator's app, open a modal asking them to log in with their ProphetX email and password.
POST https://sandbox.prophetx.dev/api/v1/auth/login{ "email": "...", "password": "..." }- If the response indicates two-factor authentication is required, call the verification-code endpoint to text/email the user a code:
POST https://sandbox.prophetx.dev/api/v1/send-verification-code-
Prompt the user for that code, then call
auth/loginagain, this time including the code, to complete the login. -
A successful login returns an
accessToken. Hold it in the client only — it is what authorizes Step 2's key-generation call.
These are the same public endpoints ProphetX's own sandbox web app (
sandbox.prophetx.dev) calls, not endpoints published in ProphetX's partner API docs. Capture the exact request/response fields (and the 2FA-required response shape) via browser dev tools network inspection againstsandbox.prophetx.devbefore building against them. Once going to production, replacehttps://sandbox.prophetx.devwithhttps://www.prophetx.coas the base URL for all APIs.
Step 2: Generate the user's trading API key/secret
Open a second modal confirming the user wants to generate a ProphetX API key for the aggregator to trade with. On confirmation, call:
POST https://sandbox.prophetx.dev/partner/auth/keys
Authorization: Bearer {accessToken from Step 1}Response
{ "access_key": "...", "secret_key": "..." }- Store the returned
access_key/secret_keyon the user's device only (e.g. secure local storage or platform keychain). Do not send it to, or persist it on, the aggregator's backend. - This is a deliberate trade-off: keeping the trading secret off the aggregator's servers limits the aggregator's exposure if its backend is ever compromised, at the cost of the aggregator's backend not being able to place orders or query order status for the user directly (see Step 6).
- This key/secret pair is what Step 6 uses to call
submit_orderand to open the user's own private WebSocket channel.
The exact request/response of
partner/auth/keyscan be captured via browser dev tools network inspection againstsandbox.prophetx.dev. This is the same endpoint used by ProphetX Web and mobile.
Step 3: Ingest event/market fixture data (aggregator backend)
Using the aggregator's own ProphetX account credentials (from "Before you start", not any user's key), pull tournaments, events, and markets so you have the identifiers you'll need later to place an order.
GET /mm/get_tournaments
GET /mm/get_sport_events?tournament_id={id}
GET /mm/get_multiple_markets?event_ids={id1,id2,...}
GET /mm/get_price_ladderRecord the following for each market selection you plan to support:
| Field | Example | Needed for |
|---|---|---|
tournament_id | 109 | Fetching events, WebSocket topic naming |
event_id | 10073803 | Fetching markets |
market type | moneyline / total / spread | Filtering which markets you expose |
sub_type | player_total_hits (optional field) | Needed to subscribe to prop-specific channels in Step 4 — fetch it via the v2/v4 get_multiple_markets endpoint version and read it defensively, since it's optional |
strike_id | 40a32ae7b39684d430cf494be26cbe6b | Required on order submission in Step 6 |
| valid price ladder | from get_price_ladder | Orders must use a price on this ladder |
Reference: docs.prophetx.co/docs/integration (Step 3: Seed tournaments, events, markets, and prices).
Step 4: Real-time order book via WebSocket (aggregator backend)
ProphetX has moved off a single shared market-data channel to dedicated channels per event and, for props, per market sub-type, over Pusher Channels. Both migration deadlines have already passed as of this writing (player props: July 29, 2026; main markets, which now also include sup_moneyline and moneyline_3_way: August 12, 2026) — the old shared channel no longer carries market_selections at all, so the aggregator's backend needs to be on the new model now, not as a future migration.
-
Get connection config:
GET /websocket/connection-config(unchanged) to get the Pusher app key/cluster. -
Open a Pusher connection with that config, and capture
socket_idfrom thepusher:connection_establishedevent. Note: itsdatafield is a JSON string containing JSON — decode it twice. -
Discover
event_id(GET /mm/get_sport_events?tournament_id={id}) and, for props,sub_typeper market (GET /v4/mm/get_multiple_markets?event_ids={ids}— use the v4 endpoint version;sub_typeis optional, so read defensively). -
Register your subscription —
POST /v4/mm/websocket(this replaces the oldPOST /v4/mm/pushercall) with a declarative, complete subscription set on every call:
{
"socket_id": "...",
"service": "pusher",
"subscriptions": [
{ "type": "event", "ids": ["18756"] },
{ "type": "event_subtype", "ids": ["18756:player_to_record_a_double_double"] }
]
}Subscribing with type "event" always gets every current main market for that event — you don't need to track the main/prop split yourself.
-
Complete the Pusher handshake: send
pusher:signinusing the authenticated block from the register response, wait forpusher:signin_success, then sendpusher:subscribeper channel using that channel's ownauth+channel_namefromauthorized_channel(each channel's auth only works for that channel), and wait forpusher_internal:subscription_succeededon each. -
Bind
market_selectionson each authorized channel and consume. Payload shape is unchanged — only the routing changed. Classify incoming messages by the response'sscopefield (event_id, andsub_typewhere present), not by channel name or event name.
Re-fetch connection-config at least every 30 minutes and reconnect if it has changed; treat a dropped socket as a signal to reconnect, get a fresh socket_id, and re-register (reconnecting also resets your channel usage). Always read channel_limit from the register response rather than hardcoding it — it's account-configured, counts the cumulative union of channels you've registered on that socket, and going over it rejects the entire request with HTTP 400 exceed_subscription_count. Subscribe selectively (e.g. not every event's full prop set) rather than assuming headroom.
This design avoids the earlier concern about needing one live connection per linked user: the aggregator's backend keeps a single connection for shared market data, and each user's own order-status updates are handled by their own client in Step 6 instead. For client order status updates, aggregators web/app needs to send that information to your backend.
Reference: docs.prophetx.co/docs/integrate-with-websockets (updated channel model, effective per the deadlines above); docs.prophetx.co/docs/websocket-events.
Step 5: Geolocation check (before every order)
Before letting a user submit an order, call ProphetX's IP-based geolocation check and only proceed if it returns success.
Sandbox: https://sandbox.prophetx.dev/ip-geolocation/api/v1/check-ip
- Call this with the user's current IP address for the order attempt.
- Proceed to Step 6 only if the response's
successfield istrue. - If it's not
true, block the order and give the user a way to retry (e.g. refresh location, try again).
The exact request/response of
ip-geolocation/api/v1/check-ipcan be captured via browser dev tools network inspection againstsandbox.prophetx.dev. This is the same endpoint used by ProphetX Web and mobile.
Step 6: Submit the order from the client
Once the geolocation check in Step 5 passes, the client — not the aggregator's backend — submits the order directly to ProphetX, using the user's own key/secret from Step 2.
POST v4/submit_order{
"external_id": "<client-generated uuid>",
"strike_id": "<from Step 3>",
"price": -122,
"quantity": 1.0,
"fill_or_kill": true
}See partner-docs.sandbox.prophetx.dev/swagger/mm/index.html for the full request schema.
external_id must be unique per order — reusing one on a retry gets it rejected, which is what prevents duplicate submission. Order aggregators will typically only submit limit orders; pair with fill_or_kill when the order shouldn't rest on the book if it doesn't fill immediately.
To show the user their own order status in real time, the client runs the same connect-and-register flow as Step 4 (Sections 2–5 of the WebSocket doc), authenticated with that user's own key/secret. It doesn't need to request any event/event_subtype subscriptions for this — the register response automatically includes a private per-account channel (private-service=6-device_type=5-user={user's account_id}) carrying a orders event; bind that on the client to get the user's own order/trade updates.
- Because the user's key/secret never reaches the aggregator's backend, the aggregator's backend has no direct visibility into individual order submissions, fills, or cancellations unless the client separately reports them back to the aggregator — flag this as an open item if the aggregator needs server-side order history or analytics.
- Cancellation follows the same client-side pattern:
POST v4/mm/cancel_orderwith theexternal_idand theorder_idreturned at submission.
Step 7: Certify and go live
Once Steps 1–6 are working end-to-end in sandbox, go through ProphetX's production transition / certification process before pointing your integration at production hosts. ProphetX will confirm certification is complete before you switch over — do not flip any base URL until you have that confirmation.
Open items
| Topic | Status |
|---|---|
| Trading API general availability | Not yet public — requires ProphetX to approve the aggregator's own API/market-maker account for Steps 3–4. |
| Login / 2FA endpoint schema | auth/login and send-verification-code are undocumented public endpoints used by ProphetX's sandbox web app. Capture the exact schema via browser network inspection against sandbox.prophetx.dev. |
partner/auth/keys schema | Capture the exact request/response via browser network inspection against sandbox.prophetx.dev — same endpoint used by ProphetX Web and mobile. |
check-ip full schema | Capture the exact request/response via browser network inspection against sandbox.prophetx.dev — same endpoint used by ProphetX Web and mobile. |
| Client-side key storage risk | The user's trading secret now lives on-device rather than server-side. Run a security review of local storage/keychain handling on each client platform the aggregator supports. |
| Backend visibility into user orders | The aggregator's backend won't see individual submissions/fills unless the client reports them back. Decide if that reporting path is needed for the aggregator's own order history or analytics. |
| Certification timeline | To be scheduled with ProphetX once sandbox testing is complete. |
References
- Trading API integration guide (fixture ingestion & WebSocket, aggregator's own account):
docs.prophetx.co/docs/integration - WebSocket integration:
docs.prophetx.co/docs/integrate-with-websockets - Order submission / market-maker API (swagger):
partner-docs.sandbox.prophetx.dev/swagger/mm/index.html - Production transition / going live:
docs.prophetx.co/docs/prophetx-service-api-switch-to-production
Updated 3 days ago
