# XMR.WIN API Documentation

> XMR.WIN is a Monero-based prediction market. Bet on real-world events with full privacy. Zero KYC.

## Base URL
`https://xmr.win/api`

## Authentication

Two ways to authenticate:

### 1. JWT Token (Browser/Session)
1. Register: `POST /api/register` (PoW captcha required)
2. Login: `POST /api/login`
3. Send the token: `Authorization: Bearer <token>`

### 2. API Key (the better fit for bots and agents)
1. Log in via JWT first
2. Create a key: `POST /api/keys`
3. Send it on every request: `X-API-Key: xmr_<32hexchars>`

API key scopes: `read`, `trade`, `wallet`, `withdraw`

`wallet` grants read-side wallet access: balance, deposit address, transaction history, and withdrawal fee estimate (GET /api/wallet/balance, /api/wallet/deposit-address, /api/wallet/transactions, /api/estimate-fee). Scope hierarchy: `withdraw` ⊇ `wallet` ⊇ `read`, and `trade` ⊇ `read` — a least-privilege read-only-balance key should use `wallet`, not `withdraw` and not plain `read`.

### PoW Captcha (Registration Only)
Signup needs a proof-of-work captcha:
1. `GET /api/captcha` → `{challenge, difficulty}`
2. Find a nonce where `SHA256(challenge + nonce)` has `difficulty` leading zero bits
3. Send `captcha_challenge` and `captcha_solution` in the register call

---

## Public Endpoints (No Auth Required)

### GET /api/markets
List prediction markets. Default: active markets, sorted by volume.

| Param | Type | Default | Description |
|-------|------|---------|-------------|
| status | string | "active" | Filter by status: active, resolved, closed |
| category | string | — | Filter by category (case-insensitive) |
| search | string | — | Search in question/description |
| sort | string | "volume" | Sort by: volume, newest, ending |
| limit | int | 20 | Max results (1-100) |
| offset | int | 0 | Pagination offset |

**Response:**
```json
{
  "markets": [
    {
      "id": "uuid",
      "question": "Will BTC reach 100k by end of 2025?",
      "description": "Resolves YES if...",
      "category": "crypto",
      "outcomes": ["Yes", "No"],
      "outcome_prices": [0.65, 0.35],
      "status": "active",
      "volume": 125.5,
      "end_date": "2025-12-31T00:00:00Z",
      "created_at": "2025-01-15T10:00:00Z"
    }
  ],
  "total_count": 42,
  "limit": 20,
  "offset": 0
}
```

### GET /api/markets/trending
Top 5 markets by 24h volume.

### GET /api/markets/resolved
Markets resolved in the last 30 days.

### GET /api/markets/categories
Every category we have.

### GET /api/markets/{id}
Full market details with order book and recent trades.

**Response includes:** market data, combined order book (bids/asks per outcome), the most recent 20 trades, and stats.

### GET /api/markets/{id}/orderbook
Order book for a single market.

**Response:**
```json
{
  "Yes": {"bids": [{"price": 0.60, "amount": 10}], "asks": [{"price": 0.65, "amount": 5}]},
  "No": {"bids": [{"price": 0.35, "amount": 8}], "asks": [{"price": 0.40, "amount": 3}]}
}
```

### GET /api/markets/{id}/trades
Recent trades on a market.

### GET /api/markets/{id}/stats
Per-market stats (volume breakdown, price history).

### GET /api/trades/recent
Most recent trades across every market.

### GET /api/stats
Platform totals (volume, users, market count).

### GET /api/leaderboard
Top traders by profit and volume.

### GET /api/fee-tiers
Fee tier breakdown.

**Response:**
```json
{
  "tiers": [
    {"name": "Standard", "min_volume_usd": 0, "fee_rate": 0.02, "fee_percent": "2.00%"},
    {"name": "Silver", "min_volume_usd": 1000, "fee_rate": 0.0175, "fee_percent": "1.75%"},
    {"name": "Gold", "min_volume_usd": 100000, "fee_rate": 0.015, "fee_percent": "1.50%"},
    {"name": "VIP", "min_volume_usd": 1000000, "fee_rate": 0.01, "fee_percent": "1.00%"}
  ],
  "fee_on": "buy",
  "description": "Fees come off the buy side only. Tiers run off your 7-day rolling trading volume in USD."
}
```

### GET /api/users/{username}
Public profile for a user.

### GET /api/users/{username}/pnl
A user's profit/loss history.

### GET /api/users/{username}/trades
A user's public trade history.

### GET /api/captcha
Fetch a proof-of-work captcha challenge for signup.

**Response:** `{"challenge": "hex_string", "difficulty": 16}` — difficulty is adaptive (16-22); read it from the response, don't hardcode it.

### GET /api/version
Backend build/version info: `{version, commit, build_time, frontend_version, draining}`.

### GET /api/banner
Public platform banner and suspension flags (deposits/withdrawals/trading/registration).

### GET /api/events
List multi-outcome events. Query params: `status` (active|resolved|closed|all, default active), `category`, `search`, `sort` (volume|newest|ending, default volume), `limit` (default 20), `offset`. Returns `{events[], limit, offset, total_count}`.

### GET /api/events/{id}
Single event with its child markets. `id` is the event ID string (not a market UUID).

### GET /api/events/{id}/price-history
Per-child-market price-history series. Query param `range` (1h|6h|1d|1w|1m|all, default 1w).

### GET /api/markets/{id}/top-holders
Top holders for a market (position and PnL per holder).

### GET /api/markets/{id}/price-history
Bucketed Yes-price history. Query param `range` (1h|6h|1d|1w|1m|all, default 1w; also accepts 24h/7d/30d aliases).

### GET /api/markets/{id}/event-siblings
Sibling markets sharing the same `event_id`.

### GET /api/crypto-markets
Current rolling crypto markets — the active market for each of the 6 lanes (BTC/ETH/XMR x 10m/1h). Returns `{markets[], now, generated_at}`.

### GET /api/crypto-markets/{laneKey}
Current market for one lane. `laneKey` is `<asset>-<timeframe>` (btc-10m, btc-1h, eth-10m, eth-1h, xmr-10m, xmr-1h).

### GET /api/crypto-markets/{id}/proof.json
Resolution proof for a crypto market (only Resolved markets return a proof).

### GET /api/prices/crypto
Live BTC/ETH/XMR price snapshot: `{prices: {<asset>: {...}}}`.

### GET /api/crypto/price-history
USD price-history series for one asset. Query params: `asset` (required, e.g. btc/eth/xmr) and `minutes` (optional, default 60, max 240). Returns `{asset, points[]}`.

### GET /api/pricefeed/status
Price-feed source diagnostics (per-source fetch counts and freshness).

### GET /api/chat/history
Public chat message history.

### GET /api/check-username
Check whether a username is still free. Query param: `username`

---

## Authenticated Endpoints

### Authentication
`POST /api/register` creates an account (captcha required)
`POST /api/login` logs in (captcha kicks in after 3 failed attempts from the same IP)
`POST /api/logout` blacklists the current token
`POST /api/refresh-token` swaps a valid token for a fresh one

### Trading

#### POST /api/orders
Place a new order. **Scope:** `trade`.

**Request Body:**
```json
{
  "market_id": "uuid",
  "outcome": "Yes",
  "side": "buy",
  "type": "limit",
  "price": 0.65,
  "amount": 10
}
```

| Field | Type | Description |
|-------|------|-------------|
| market_id | uuid | Market ID |
| outcome | string | Has to match the market's outcome name exactly (e.g. "Yes", "No") |
| side | string | "buy" or "sell" |
| type | string | "limit" or "market" |
| price | float | 0.0001-0.9999, for limit orders |
| amount | float | Number of shares |

**Response (201):**
```json
{
  "order": {"id": "uuid", "status": "filled", "filled": 10, ...},
  "trades": [{"price": 0.65, "amount": 10, ...}],
  "message": "Order filled"
}
```

Order statuses: `open` (resting on the book), `filled` (complete), `partial` (partially filled, rest on the book), `cancelled` (market order with no liquidity to fill it).

#### DELETE /api/orders/{id}
Cancel an open order. **Scope:** `trade`. The reserved balance comes back.

#### GET /api/orders/history
Your order history.

### Portfolio

#### GET /api/portfolio
Every position across every market. **Scope:** `read`.

#### GET /api/portfolio/fills
Per-fill P&L history for the trade-history view. **Scope:** `read`.

#### GET /api/profile
Your profile info. **Scope:** `read`.

#### PUT /api/profile
Update your profile (bio, is_private). **Auth:** JWT.

### Wallet

#### GET /api/wallet/balance
XMR balance. **Scope:** `wallet`.

**Response:** `{"balance": 5.123456789012}`

#### GET /api/wallet/deposit-address
XMR deposit address. **Scope:** `wallet`.

**Response:** `{"address": "4...", "index": 5}`

#### POST /api/wallet/withdraw
Withdraw XMR to an external address. **Scope:** `withdraw`.

**Request Body:**
```json
{
  "address": "4...",
  "amount": 1.5
}
```

#### GET /api/wallet/transactions
Your deposit and withdrawal history. **Scope:** `wallet`.

#### GET /api/estimate-fee
Estimate the network fee and total for a withdrawal. **Scope:** `wallet`. Query param `amount` (XMR).

### Non-XMR funding

There's no platform API for non-XMR deposits or withdrawals anymore. The
web UI embeds Trocador's third-party swap widget on the Wallet page for
people who want to deposit other coins. That flow runs entirely on the
frontend through Trocador and isn't exposed under our API.

### Chat

#### POST /api/chat
Send a chat message. **Auth:** JWT.

**Request Body:** `{"content": "Hello everyone!"}`

### Comments

#### GET /api/markets/{id}/comments
List comments on a market (public). Query params `limit` (default 50) and `offset`.

#### POST /api/markets/{id}/comments
Create a comment on a market. **Auth:** JWT only (API keys are rejected with 403). **Request Body:** `{"content": "...", "parent_id": "<uuid optional>"}`. Returns 201.

#### DELETE /api/comments/{commentId}
Delete one of your own comments. **Auth:** JWT.

#### POST /api/comments/{id}/report
Report a comment for moderation. **Auth:** JWT.

### Referral

#### GET /api/referral/stats
Referral stats (referred users count, earnings).

#### GET /api/referral/earnings
Per-credit referral earnings history.

### Fee Info

#### GET /api/my-fees
Your current fee tier, 7-day volume, and fee rate. **Scope:** `read`.

**Response:**
```json
{
  "current_tier": "Silver",
  "fee_rate": 0.0175,
  "fee_percent": "1.75%",
  "volume_7d_xmr": 15.5,
  "volume_7d_usd": 2325.0,
  "xmr_usd_rate": 150.0,
  "next_tier_volume_usd": 100000,
  "fee_on": "buy",
  "tiers": [...]
}
```

### Favorites/Watchlist

#### GET /api/favorites
Your favorited markets with full details.

#### GET /api/favorites/ids
Just the IDs of your favorited markets.

#### POST /api/favorites/{marketId}
Add a market to favorites.

#### DELETE /api/favorites/{marketId}
Take a market off favorites.

### Settings

#### PUT /api/settings/password
Change your password. **Auth:** JWT.

**Request Body:** `{"current_password": "old", "new_password": "new"}`

---

## API Key Management (JWT auth only)

### POST /api/keys
Create a new API key.

**Request Body:**
```json
{
  "name": "My Trading Bot",
  "permissions": ["read", "trade"]
}
```

**Response:** (key shown only once!)
```json
{
  "key": "xmr_a1b2c3d4e5f6...",
  "id": "uuid",
  "name": "My Trading Bot",
  "prefix": "a1b2c3d4"
}
```

### GET /api/keys
List every API key on your account. (Also available at `GET /api/settings/keys`.)

### DELETE /api/keys/{id}
Revoke an API key.

---

## WebSocket

Connect to `wss://xmr.win/ws` for live updates. Auth is optional: after connecting send `{"type":"auth","token":"<jwt>"}`.

**Subscribe to channels:**
```json
{"type": "subscribe", "channel": "orderbook:<market_id>"}
{"type": "subscribe", "channel": "trades:<market_id>"}
{"type": "subscribe", "channel": "prices"}
{"type": "subscribe", "channel": "chat"}
```

**Events:**
- `orderbook_update` — order book snapshot/delta for `orderbook:<id>`
- `trade` — a trade executed, for `trades:<id>`
- `prices` — price map update, for the `prices` channel
- `chat_message` — new chat message, for the `chat` channel

---

## Rate Limits

| Scope | Limit |
|-------|-------|
| General API | 120 requests/minute per IP |
| Auth endpoints | 20 requests/minute per IP |

Rate limit headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`

---

## Fee Structure

Fees come off the **buy side only**. Tiered by 7-day rolling trading volume in USD:

| Tier | 7-Day Volume (USD) | Fee Rate |
|------|-------------------|----------|
| Standard | $0 - $1,000 | 2.00% |
| Silver | $1,000 - $100,000 | 1.75% |
| Gold | $100,000 - $1,000,000 | 1.50% |
| VIP | $1,000,000+ | 1.00% |

Non-XMR deposits through the Trocador widget pay Trocador's own quote /
spread / fees. Those are separate from the platform fee schedule above.

---

## Error Format
```json
{"error": "description of what went wrong"}
```

| Status | Meaning |
|--------|---------|
| 200 | OK |
| 201 | Created (order placed) |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized (missing/invalid auth) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 429 | Rate Limited |
| 500 | Server Error |

---

## Quick Start for LLM Agents

1. Sign up at https://xmr.win (solve the PoW captcha in the browser)
2. Log in: `POST /api/login` with username and password
3. Create an API key: `POST /api/keys` with the scopes you want
4. Send the `X-API-Key` header on everything after that
5. Browse markets: `GET /api/markets`
6. Check the order book: `GET /api/markets/{id}/orderbook`
7. Place an order: `POST /api/orders`
8. Deposit: `GET /api/wallet/deposit-address` (direct XMR. For non-XMR deposits, use the Trocador widget on the web UI; there's no API for it.)
9. Watch your positions: `GET /api/portfolio`

### Important Notes for Agents
- Every amount is in XMR (Monero)
- Outcome names are case-sensitive ("Yes", not "yes")
- Limit prices have to fall between 0.0001 and 0.9999
- Markets run a CLOB (Central Limit Order Book) with complementary matching
- Yes price + No price converges to 1.0 XMR per share
- Winners get 1.0 XMR per winning share at resolution
