---
title: Quick Start
description: Create API credentials, call a public endpoint, sign a private request, and place your first order in the UAT environment.
sidebar:
  order: 2
---

## Choose API endpoints

| Environment | REST endpoint | WebSocket endpoint |
| --- | --- | --- |
| UAT | `https://api.uat.udx.underdogsports.com/` | `wss://api.uat.udx.underdogsports.com/ws` |
| Production | `https://api.udx.underdogsports.com/` | `wss://api.udx.underdogsports.com/ws` |

Set the UAT REST base URL once before running the examples:

```bash
export UDX_BASE_URL="https://api.uat.udx.underdogsports.com"
```

## List markets

The markets catalog is public and does not require authentication:

```bash
curl "${UDX_BASE_URL}/markets?limit=1&sort=event_date&order=asc"
```

Use a returned `ticker` to request its aggregated order book:

```bash
curl "${UDX_BASE_URL}/markets/UDX-EXAMPLE-MARKET/orderbook?depth=10"
```

See [Pagination](/pagination) for additional pages and the [WebSocket API](/websocket-api) for live updates.

## Create an API Key

Open the [UDX UAT app](https://app.uat.udx.underdogsports.com/) and create an API key with the `trade` scope and access to the account you will use. Save the API key ID and private key, then set their values in your environment:

```bash
export UDX_ACCESS_KEY="<api-key-id>"
export UDX_PRIVATE_KEY_PATH="<path-to-private-key.pem>"
```

## Place an order

This example uses Node.js 18 or newer and only built-in modules. Save it as `place-order.mjs`, replace the example ticker with one returned by the markets request, then run `node place-order.mjs`:

```js place-order.mjs
import {
  constants,
  createHash,
  createPrivateKey,
  randomUUID,
  sign,
} from "node:crypto";
import { readFileSync } from "node:fs";

const baseUrl = process.env.UDX_BASE_URL;
const accessKey = process.env.UDX_ACCESS_KEY;
const privateKeyPath = process.env.UDX_PRIVATE_KEY_PATH;

if (!baseUrl || !accessKey || !privateKeyPath) {
  throw new Error(
    "Set UDX_BASE_URL, UDX_ACCESS_KEY, and UDX_PRIVATE_KEY_PATH first.",
  );
}

const method = "POST";
const path = "/orders";
const timestamp = Date.now().toString();
const idempotencyKey = randomUUID();
const body = JSON.stringify({
  ticker: "UDX-EXAMPLE-MARKET",
  client_order_id: "first-order",
  side: "buy",
  quantity: "1",
  price: "0.55",
  time_in_force: "gtc",
});

const bodyHash = createHash("sha256").update(body).digest("hex");
const message = timestamp + method + path + bodyHash + idempotencyKey;
const privateKey = createPrivateKey(
  readFileSync(privateKeyPath, "utf8"),
);
const signature = sign("sha256", Buffer.from(message), {
  key: privateKey,
  padding: constants.RSA_PKCS1_PSS_PADDING,
  saltLength: 32,
}).toString("base64");

const response = await fetch(`${baseUrl}${path}`, {
  method,
  headers: {
    "Content-Type": "application/json",
    "UDX-Access-Key": accessKey,
    "UDX-Access-Timestamp": timestamp,
    "UDX-Access-Signature": signature,
    "UDX-Idempotency-Key": idempotencyKey,
  },
  body,
});

const result = await response.json();
if (!response.ok) {
  throw new Error(`${response.status}: ${JSON.stringify(result)}`);
}

console.log(result);
```

The timestamp is the current Unix time in milliseconds. For this first order, always send the newly generated UUID in `UDX-Idempotency-Key` and include its exact value in the signed message. Reuse that key only to retry this same order. See [Authentication](/authentication) for the complete signing rules, [Idempotency](/idempotency) for optional-header behavior outside this walkthrough, and [Place an order](/api-reference/orders/createorder) for the full request and response.

:::note[Concepts]
Learn how [participants and access](/concepts/participants-and-access) determine which accounts a credential can use.
:::
