Skip to content
UDX
On this page

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, a cmd, and params. Every direct response echoes that id.
  • subscribe opens one subscription per (channel, ticker) pair. Each pair receives its own subscribed response with a server-assigned subscription ID (sid).
  • Each subscription has its own seq, which starts at 1 and increments once per frame.
  • unsubscribe ends a subscription by sid. Its unsubscribed response echoes the command id and is a barrier after which no frame for that sid arrives.

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.