Connecting
Reach UDX over HTTPS or WebSocket in each environment, and understand what a request or a session is.
UDX exposes the same environments over REST and WebSocket. Use UAT to build and test; production carries real money.
| Environment | REST endpoint | WebSocket endpoint |
|---|---|---|
| UAT | https://api.uat.udx.underdogsports.com/ |
wss://api.uat.udx.underdogsports.com/ws |
| Production | https://api.underdogexchange.com/ |
wss://api.underdogexchange.com/ws |
Create your UAT API key in the UDX UAT app. Both interfaces authenticate with the same API key and select a dated contract version. See Authentication and Versioning.
Make a Request
Every REST request stands alone. There is no session: each request carries its own credential, its own UDX-Version, and, for a signed request, a fresh timestamp and signature. Request and response bodies are JSON, and a request with a body sends Content-Type: application/json.
Open GET /ws and offer a supported udx.json.<YYYY-MM-DD> subprotocol; the exchange selects it, and a connection that offers none it supports is refused. Authenticate the upgrade with the three UDX-Access-* headers, signing timestamp + GET + /ws. The session authenticates once, at connect; there is no per-message signature, and nothing sent afterward changes who you are.
The connection acts for the API key’s member. Member-private streams, such as RFQs, return only that member’s data. Every frame is JSON text; a binary frame closes the connection.
A command is a JSON object with a client-assigned id, a cmd, and params:
{
"id": 1,
"cmd": "subscribe",
"params": {
"channels": [
{
"channel": "rfqs",
"tickers": ["UDX-EXAMPLE-MARKET-A"]
}
]
}
}id is your correlation handle: a positive integer, unique among the commands you have in flight. Every direct response echoes it. Streamed data carries no id.
Read Responses
Every response carries the applied UDX-Version. Collections page through next_page_url, described in Pagination. Trading writes answer 202 Accepted and resolve later, described in Asynchronous Writes.
- Every command is a JSON object containing a client-assigned
id, acmd, andparams. Every direct response echoes thatid. subscribeopens one subscription per(channel, ticker)pair. Each pair receives its ownsubscribedresponse with a server-assigned subscription ID (sid).- Each subscription has its own
seq, which starts at1and increments once per frame. unsubscribeends a subscription bysid. Itsunsubscribedresponse echoes the commandidand is a barrier after which no frame for thatsidarrives.
See the WebSocket API reference for more details.
Handle Errors
Every non-2xx response is a problem details body as defined by RFC 9457, sent as application/problem+json. Every body contains type, title, status, detail, and an instance that names the request ID. type is a stable urn:udx:problem: URN that identifies the kind of problem; it is an identifier, not a URL to dereference. Branch on the status code and type. detail is human-readable and may change. Quote the instance value when you contact UDX support.
For example, an invalid request parameter returns:
{
"type": "urn:udx:problem:invalid-request-parameters",
"title": "Bad Request",
"status": 400,
"detail": "invalid request parameters",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}A rejected command returns type: "error" echoing the command’s id and leaves the connection open:
{
"id": 1,
"type": "error",
"msg": {
"code": "permission_denied",
"detail": "..."
}
}A stream can also end on its own. An error frame that names a sid and carries no id is terminal for that subscription: no further frame arrives for it, the connection and every other subscription stay usable, and you resubscribe when the cause clears. Each operation’s reference page lists the codes it can send. A connection-fatal condition is reported with a WebSocket close frame instead; see Close Codes.
Stay Connected
REST holds no connection state. Each request authenticates itself, so there is nothing to keep alive and nothing to resume.
The exchange keeps each session alive with standard RFC 6455 ping/pong control frames. There is no application-level heartbeat message.
- The exchange sends the pings. Your client must answer each with a pong. Conformant WebSocket libraries do this automatically.
- You may also send your own pings; the exchange answers each with a pong.
- A ping not answered within the keep-alive interval closes the session with code
1001. The interval is configuration-derived; do not hard-code it, and always answer pings.
A sid and its sequence exist only for the connection. A disconnected session cannot be resumed: reconnect, authenticate again, resubscribe, and rebuild state.