River Markets orderbooks API
The orderbooks API from River Markets — 2 operation(s) for orderbooks.
The orderbooks API from River Markets — 2 operation(s) for orderbooks.
openapi: 3.1.0
info:
title: River Markets balance orderbooks API
description: "# Getting Started\n\n\nRiver Markets provides users with a unified prime brokerage interface for prediction markets, including\n- Liquidity aggregation\n- Order routing\n- Market and data search\n\nThis API guide will help you get up and running quickly.\n\n## Concepts\n\n### River IDs\n\nEvery market across all exchanges is assigned a unique **River ID** - a unified identifier that works across our entire platform. This allows you to:\n\n- Reference the same market consistently regardless of which exchange it trades on\n- Place orders using a single identifier format\n- Track positions and fills across exchanges with one ID\n\nWhen searching for markets, the API returns both the exchange-native identifier (e.g., Kalshi ticker, Polymarket slug) and the River ID. Use the River ID when placing orders.\n\n### Subaccounts\n\n**Subaccounts** let you organize your trading activity into separate containers. Each subaccount:\n\n- Has its own set of exchange credentials\n- Maintains separate positions and order history\n- Can represent different strategies, clients, or portfolios\n\nThis is useful for:\n- **Fund managers**: Segregate client portfolios\n- **Traders**: Separate strategies (e.g., momentum vs. mean reversion)\n- **Teams**: Give team members isolated trading environments\n\n## 1. Create an Account\n\nSign up at [app.rivermarkets.com](https://app.rivermarkets.com) to create your account.\n\n## 2. Generate an API Key\n\n1. Log in to your dashboard\n2. Navigate to **Settings → API Keys**\n3. Click **Create API Key**\n4. Copy and securely store your key - it will only be shown once\n\n## 3. Install required packages\n\n```python\npip install requests\n```\n\n## 4. Make Your First Request\n\nTest your API key by searching for markets.\n\nWe support ticker (kalshi ticker, polymarket condition_id/slug, river_id) and fuzzy search ('bitcoin', 'fed rates').\n\n> **Using the SDK?** `rivermarkets` (Python) \n> handle request signing for you — pass your `KEY_ID` and `PRIVATE_KEY` to the\n> client constructor and forget about the rest of this section. The raw example\n> below is for anyone integrating without an SDK.\n\n```python\nimport base64, hashlib, time\nfrom urllib.parse import urlsplit, urlencode, parse_qsl, quote\nfrom nacl.signing import SigningKey\nimport requests\n\nKEY_ID = \"<uuid from Settings → API Keys>\"\nPRIVATE_KEY_B64 = \"<base64 private key shown once at creation>\"\nBASE_URL = \"https://api.rivermarkets.com/v1\"\n\nsigning_key = SigningKey(base64.b64decode(PRIVATE_KEY_B64))\n\n\ndef sign(method: str, url: str, body: bytes = b\"\") -> dict:\n parts = urlsplit(url)\n # quote_via=quote encodes spaces as %20 to match RFC 3986 / the server.\n sorted_q = urlencode(sorted(parse_qsl(parts.query, keep_blank_values=True)), quote_via=quote)\n ts = str(int(time.time()))\n canonical = \"\\n\".join([\n method.upper(), parts.path, sorted_q, ts, hashlib.sha256(body).hexdigest(),\n ]).encode()\n sig = signing_key.sign(canonical).signature\n return {\n \"X-River-Key-Id\": KEY_ID,\n \"X-River-Timestamp\": ts,\n \"X-River-Signature\": base64.b64encode(sig).decode(),\n }\n\n\nurl = f\"{BASE_URL}/markets/search?q=shutdown&exchange=POLYMARKET\"\nresponse = requests.get(url, headers=sign(\"GET\", url))\nfor market in response.json()[\"results\"]:\n print(f\"River ID: {market['river_id']}, Ticker: {market['slug']}\")\n```\n\n## 5. Set Up a Subaccount\n\nRiver allows users to setup subaccounts - which allow them to manage different trading strategies or client portfolios.\n\nThese can be added through the UI by going to Settings -> Subaccounts\nor programatically.\n\n### Video Walkthrough\n\nWatch this tutorial to see how to create a subaccount, add a Polymarket account, and place your first trade:\n\n<div style=\"position: relative; padding-bottom: 54.37499999999999%; height: 0;\"><iframe src=\"https://www.loom.com/embed/9b49e269c09e436d98b72f3cb55d7691\" frameborder=\"0\" webkitallowfullscreen mozallowfullscreen allowfullscreen style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%;\"></iframe></div>\n\n### Programmatic Setup \n\n```python\n# Create a subaccount\nresponse = requests.post(\n f\"{BASE_URL}/subaccounts\",\n headers=headers,\n json={\"name\": \"My Trading Account\"}\n)\nsubaccount = response.json()\nsubaccount_id = subaccount[\"id\"]\n```\n\n## 6. Add Exchange Credentials\n\nExchange credentials can also be added via UI under Settings -> Subaccounts,\nor programatically as shown below.\n\nFor best results, we recommend\n- Polymarket : create an account using email (not a wallet) and get your wallet and PK directly from Polymarket under [Settings -> Export Private Key](https://polymarket.com/settings)\n- Kalshi : create a secret/key pair with Read/Write access under [Settings -> Account & security -> API keys](https://kalshi.com/account/profile)\n\n```python\n# Add Polymarket credentials\nresponse = requests.post(\n f\"{BASE_URL}/credentials\",\n headers=headers,\n json={\n \"subaccount_id\": subaccount_id,\n \"exchange\": \"polymarket\",\n \"wallet_address\": \"your_polymarket_wallet_address\",\n \"private_key\": \"your_polymarket_private_key\"\n }\n)\n# Add Kalshi credentials\nresponse = requests.post(\n f\"{BASE_URL}/credentials\",\n headers=headers,\n json={\n \"subaccount_id\": subaccount_id,\n \"exchange\": \"kalshi\",\n \"api_key\": \"your_kalshi_api_key\",\n \"api_secret\": \"your_kalshi_secret\"\n }\n)\n```\n\nWe use asymmetric encryption so your credentials are not transferred between services or stored in plain text.\nWe delete your credentials when you delete a subaccount. \n\n## 7. Place Your First Order\n\nUse the River ID from your market search to place an order.\n\n```python\n# Place a limit order (buy 10 contracts at $0.50)\nresponse = requests.post(\n f\"{BASE_URL}/orders\",\n headers=headers,\n json={\n \"subaccount_id\": subaccount_id,\n \"river_id\": 4552150, # From market search\n \"order_type\": \"LIMIT\", # LIMIT or MARKET\n \"time_in_force\": \"GTC\", # FOK, GTC, GTD, or IOC\n \"buy_flag\": True, # True=buy, False=sell\n \"price\": 0.50, # Limit price (0-1)\n \"qty\": 10, # Number of contracts\n }\n)\norder = response.json()\nprint(f\"Order placed: {order['river_order_id']}\")\n```\n\n## 8. Check Order Status\n\nMonitor your order's progress by fetching its current state.\n\n```python\n# Get order status\norder_id = order['river_order_id']\nresponse = requests.get(\n f\"{BASE_URL}/orders/{order_id}\",\n headers=headers\n)\norder_status = response.json()\nprint(f\"Status: {order_status['status']}, Filled: {order_status['traded_qty']}/{order_status['qty']}\")\n```\n\nPossible order statuses:\n- `pending_submission` - Order received, awaiting processing\n- `resting` - Order live on the exchange (may include partially-filled live orders)\n- `filled` - Terminal: order fully executed\n- `partially_filled` - Terminal: order cancelled with some quantity filled\n- `cancelled` - Terminal: order cancelled with no fills\n- `rejected` - Order rejected (see `reject_reason`)\n\n## 9. Cancel an Order\n\nCancel a resting order before it's fully filled.\n\n```python\n# Cancel an order\norder_id = order['river_order_id']\nresponse = requests.delete(\n f\"{BASE_URL}/orders/{order_id}\",\n headers=headers\n)\nif response.status_code == 200:\n print(\"Order cancelled successfully\")\n```\n\nNote: Only orders with status `pending_submission` or `resting` can be cancelled (`partially_filled` is terminal).\n\n## Supported Exchanges\n\n| Exchange | Markets | Status |\n|----------|---------|--------|\n| Kalshi | US regulated prediction markets | Available |\n| Polymarket | Crypto prediction markets | Available |\n| Polymarket US | US regulated prediction markets | Available |\n| Novig | US regulated prediction markets | Coming soon |\n| Rothera | US regulated prediction markets | Future integration |\n\n## Rate Limits\n\n- **Standard**: 10 requests/second\n- **Premium**: Contact support for higher limits\n\n\n\n## Authentication\n\nAll endpoints require authentication via one of:\n- **Bearer Token**: `Authorization: Bearer <jwt_token>` (for web clients)\n- **Signed Request** (programmatic access): three headers per REST request, or\n three query params on the WebSocket handshake.\n\n> **Using the SDK?** `rivermarkets` (Python) \n> sign every REST request and WS handshake for you. You only need the raw\n> recipes below if you're integrating without an SDK.\n\n### REST signing\n\nSend three headers on every request:\n- `X-River-Key-Id`: UUID of your API key\n- `X-River-Timestamp`: current unix seconds (must be within 30s of server time)\n- `X-River-Signature`: base64 Ed25519 signature over the canonical request\n\nThe REST canonical string is LF-joined:\n```\nMETHOD\\nPATH\\nSORTED_QUERY\\nTIMESTAMP\\nSHA256(body) hex\n```\n\n```python\nimport base64, hashlib, time\nfrom urllib.parse import urlsplit, urlencode, parse_qsl, quote\nfrom nacl.signing import SigningKey\n\nsigning_key = SigningKey(base64.b64decode(PRIVATE_KEY_B64))\n\ndef sign(method: str, url: str, body: bytes = b\"\") -> dict:\n parts = urlsplit(url)\n # quote_via=quote encodes spaces as %20 to match RFC 3986 / the server.\n sorted_q = urlencode(sorted(parse_qsl(parts.query, keep_blank_values=True)), quote_via=quote)\n ts = str(int(time.time()))\n canonical = \"\\n\".join([\n method.upper(), parts.path, sorted_q, ts, hashlib.sha256(body).hexdigest(),\n ]).encode()\n sig = signing_key.sign(canonical).signature\n return {\n \"X-River-Key-Id\": KEY_ID,\n \"X-River-Timestamp\": ts,\n \"X-River-Signature\": base64.b64encode(sig).decode(),\n }\n```\n\n### WebSocket signing\n\nBrowsers can't set custom headers on the WS upgrade, so the same three values\nmove to query params: `?key_id=&ts=&sig=`. The WS canonical string omits the\nbody hash and uses the literal `\"WS\"` as the method:\n```\nWS\\nPATH\\nSORTED_QUERY (excluding key_id/ts/sig)\\nTIMESTAMP\n```\n\n```python\nimport asyncio, base64, time\nfrom urllib.parse import urlencode, quote\nimport websockets\nfrom nacl.signing import SigningKey\n\nsigning_key = SigningKey(base64.b64decode(PRIVATE_KEY_B64))\n\n\ndef sign_ws(path: str, extra_params: dict | None = None) -> str:\n \"\"\"Return a fully-formed wss:// URL with signing query params appended.\"\"\"\n params = dict(extra_params or {})\n sorted_q = urlencode(sorted(params.items()), quote_via=quote)\n ts = str(int(time.time()))\n canonical = \"\\n\".join([\"WS\", path, sorted_q, ts]).encode()\n sig = base64.b64encode(signing_key.sign(canonical).signature).decode()\n params.update({\"key_id\": KEY_ID, \"ts\": ts, \"sig\": sig})\n return f\"wss://api.rivermarkets.com{path}?{urlencode(params)}\"\n\n\nasync def stream_orders(subaccount_id: str):\n url = sign_ws(\"/v1/ws/orders\", {\"subaccount_id\": subaccount_id})\n async with websockets.connect(url) as ws:\n async for frame in ws:\n print(frame)\n\n\nasyncio.run(stream_orders(\"411b3f8c-85f9-41c9-90b7-3ff6d74efc4f\"))\n```\n\n### Security notes\n\n- Your private key never traverses the wire. Generate a keypair at\n `Settings → API Keys`; the private key is shown once.\n- Same signature cannot be submitted twice within 60 seconds (replay protection).\n On network retries, re-sign with a fresh timestamp.\n- Timestamps must be within 30s of server time. Sync your clock via NTP.\n\n\n---\n\n\n\n# Order Types Documentation\n\nComplete guide to all order types available in the River Markets trading platform.\n\n---\n\n## Basic Order Types\n\n### Market Order\nExecutes immediately at the best available price.\n- **Use case**: When you need immediate execution and price is less important\n- **Example**: Market buy 100 contracts → fills at current best ask price\n\n### Limit Order\nRests on the order book at a specified price.\n- **Use case**: When you want price control and can wait for execution\n- **Example**: Limit buy 100 @ $0.65 → only fills at $0.65 or better\n\n---\n\n## Conditional Order Types\n\nConditional orders are **not placed immediately**. They activate (\"trigger\") when specific conditions are met.\n\n### 1. Stop Orders (STOP)\n\n**Stop orders trigger when price moves AGAINST your desired direction.** Used primarily for **stop losses** (exit protection) or **breakout entries**.\n\n#### Stop Market\n- **Triggers**: When market trades at or through your stop price\n- **Executes**: As a market order (immediate fill at best available price)\n- **Example**:\n ```\n You're LONG at $0.70, want to exit if price falls to $0.60\n → Stop Market SELL @ $0.60\n → Triggers when price ≤ $0.60\n → Sends market sell order → fills immediately\n ```\n\n#### Stop Limit\n- **Triggers**: When market trades at or through your stop price\n- **Executes**: As a limit order at your specified limit price\n- **Example**:\n ```\n You're LONG at $0.70, want to exit around $0.60 but not worse than $0.59\n → Stop Limit SELL @ $0.60, Limit $0.59\n → Triggers when price ≤ $0.60\n → Sends limit sell order @ $0.59 → waits for fill at $0.59 or better\n ```\n- **Risk**: May not fill if price moves too fast through your limit\n\n**Directional Logic:**\n- **BUY stop**: Triggers at or **above** stop price (breakout entry or short exit)\n- **SELL stop**: Triggers at or **below** stop price (stop loss or long exit)\n\n---\n\n### 2. Reverse Stop Orders (REVERSE_STOP)\n\n**Reverse stop orders trigger when price moves IN your desired direction.** Used exclusively for **take profit** scenarios.\n\n**Key Concept**: A \"reverse stop\" is called \"reverse\" because it triggers in the OPPOSITE direction of a normal stop order.\n\n#### Reverse Stop Market (TP Market)\n- **Triggers**: When market trades at or through your target price (OPPOSITE direction from normal stop)\n- **Executes**: As a market order\n- **Example**:\n ```\n You're LONG at $0.60, want to take profit if price rises to $0.80\n → TP Market (Reverse Stop Market) SELL @ $0.80\n → Normal stop would trigger BELOW, but reverse stop triggers ABOVE\n → Triggers when price ≥ $0.80\n → Sends market sell order → fills immediately\n ```\n\n**Directional Logic (REVERSED from normal stop):**\n- **BUY reverse stop**: Triggers at or **below** target (opposite of normal buy stop)\n- **SELL reverse stop**: Triggers at or **above** target (opposite of normal sell stop)\n\n**Why \"reverse\"?**\n- Normal **SELL stop** @ $0.60 triggers when price ≤ $0.60 (price falling)\n- **SELL reverse stop** @ $0.80 triggers when price ≥ $0.80 (price rising)\n- The trigger direction is reversed!\n\n#### Reverse Stop Limit (TP Limit)\n- **Triggers**: When market trades at or through your target price\n- **Executes**: As a limit order at your specified limit price\n- **Example**:\n ```\n You're LONG at $0.60, want to take profit around $0.80 but not worse than $0.79\n → TP Limit (Reverse Stop Limit) SELL @ $0.80, Limit $0.79\n → Triggers when price ≥ $0.80\n → Sends limit sell order @ $0.79 → waits for fill at $0.79 or better\n ```\n\n---\n\n### 3. Take Profit Orders (TP)\n\n**Parent-child orders**: Automatically triggers a child order when a parent order **fills**.\n\n#### TP Market\n- **Triggers**: When parent order is **fully filled**\n- **Executes**: Immediately places the child order (can be another TP/SL or direct order)\n- **Example**:\n ```\n Place limit buy 100 @ $0.60 WITH TP Market @ $0.80\n → When buy fills → immediately activates TP Market SELL @ $0.80\n → That TP Market is a reverse stop that triggers when price ≥ $0.80\n ```\n\n#### TP Limit\n- **Triggers**: When parent order is **fully filled**\n- **Executes**: Activates TP Limit order (reverse stop limit)\n- **Example**:\n ```\n Place limit buy 100 @ $0.60 WITH TP Limit @ $0.80 (limit $0.79)\n → When buy fills → activates TP Limit SELL\n → Triggers when price ≥ $0.80 → sends limit sell @ $0.79\n ```\n\n---\n\n### 4. Stop Loss Orders (SL)\n\n**Parent-child orders**: Automatically triggers a child order when a parent order **fills**.\n\n#### SL Market\n- **Triggers**: When parent order is **fully filled**\n- **Executes**: Immediately places the child order (can be another SL/TP or direct order)\n- **Example**:\n ```\n Place limit buy 100 @ $0.60 WITH SL Market @ $0.55\n → When buy fills → immediately activates SL Market SELL @ $0.55\n → That SL Market is a stop order that triggers when price ≤ $0.55\n ```\n\n#### SL Limit\n- **Triggers**: When parent order is **fully filled**\n- **Executes**: Activates SL Limit order (stop limit)\n- **Example**:\n ```\n Place limit buy 100 @ $0.60 WITH SL Limit @ $0.55 (limit $0.54)\n → When buy fills → activates SL Limit SELL\n → Triggers when price ≤ $0.55 → sends limit sell @ $0.54\n ```\n\n---\n\n## Order Type Comparison\n\n| Type | Trigger Condition | Execution | Primary Use Case |\n|------|------------------|-----------|------------------|\n| **Market** | None (immediate) | Immediate at best price | Need immediate fill |\n| **Limit** | None (rests) | At limit price or better | Want price control |\n| **Stop Market** | Price moves AGAINST you | Market order (immediate) | Stop loss, breakout entry |\n| **Stop Limit** | Price moves AGAINST you | Limit order (may not fill) | Stop loss with price protection |\n| **TP Market** | Parent fills + price moves FOR you | Market order via reverse stop | Take profit with immediate exit |\n| **TP Limit** | Parent fills + price moves FOR you | Limit order via reverse stop | Take profit with price control |\n| **SL Market** | Parent fills + price moves AGAINST you | Market order via stop | Stop loss from entry |\n| **SL Limit** | Parent fills + price moves AGAINST you | Limit order via stop | Stop loss with price floor |\n\n---\n\n## Key Concepts\n\n### Stop vs Reverse Stop\n- **Stop**: Triggers when price moves AGAINST you (exit losses, enter breakouts)\n- **Reverse Stop**: Triggers when price moves FOR you (take profits)\n\n### Parent-Child Orders\n- **TP/SL orders** are \"meta-orders\" that activate child orders when a parent fills\n- The child order itself is usually a stop or reverse stop order\n- This creates a two-step process: Parent fill → Child activation → Price trigger → Execution\n\n### Trigger Direction Summary\n| Order Side | Stop (Loss/Breakout) | Reverse Stop (Profit) |\n|------------|---------------------|----------------------|\n| **BUY** | Triggers ≥ stop price | Triggers ≤ target price |\n| **SELL** | Triggers ≤ stop price | Triggers ≥ target price |\n\n---\n\n## Examples\n\n### Complete Trading Scenario\n```\n1. Place limit BUY 100 @ $0.60\n WITH TP Market @ $0.80 (reverse stop)\n AND SL Market @ $0.55 (normal stop)\n\n2. Buy fills at $0.60\n → TP Market SELL @ $0.80 activated (reverse stop)\n → SL Market SELL @ $0.55 activated (normal stop)\n\n3a. If price rises to $0.80:\n → TP triggers (price ≥ $0.80) → market sell executes\n → SL cancelled automatically\n\n3b. If price falls to $0.55:\n → SL triggers (price ≤ $0.55) → market sell executes\n → TP cancelled automatically\n```\n\n### Breakout Entry with Protection\n```\nPrice is at $0.70, resistance at $0.75, support at $0.65\n\n1. Place Stop Market BUY @ $0.76 (breakout entry)\n → Triggers when price ≥ $0.76 (breaks resistance)\n → Market buy executes\n\n2. Add SL Market @ $0.74 to the stop order\n → When breakout buy fills → SL activates\n → Protects if breakout fails\n```\n\n"
contact:
name: River Markets
url: https://rivermarkets.com/
license:
name: Proprietary
version: 1.0.0
servers:
- url: https://api.rivermarkets.com
security:
- SignedRequestKeyId: []
SignedRequestTimestamp: []
SignedRequestSignature: []
- BearerAuth: []
tags:
- name: orderbooks
paths:
/v1/orderbooks/generic-asset/{generic_asset_id}:
get:
tags:
- orderbooks
summary: Get Generic Asset Orderbook
description: 'Get merged orderbook for a generic asset (basket).
Fetches orderbooks for all member instruments and merges them by summing
quantity at each price level. Touch mechanism ensures subscriptions are active.'
operationId: get_generic_asset_orderbook_v1_orderbooks_generic_asset__generic_asset_id__get
parameters:
- name: generic_asset_id
in: path
required: true
schema:
type: string
format: uuid
title: Generic Asset Id
- name: levels
in: query
required: false
schema:
type: integer
maximum: 10
minimum: 1
description: Number of price levels to return
default: 10
title: Levels
description: Number of price levels to return
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/OrderbookResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/v1/orderbooks/{river_id}:
get:
tags:
- orderbooks
summary: Get Orderbook
description: Get orderbook for a river_id.
operationId: get_orderbook_v1_orderbooks__river_id__get
parameters:
- name: river_id
in: path
required: true
schema:
type: integer
title: River Id
- name: levels
in: query
required: false
schema:
type: integer
maximum: 10
minimum: 1
description: Number of price levels to return
default: 10
title: Levels
description: Number of price levels to return
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/OrderbookResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
OrderbookStatus:
type: string
enum:
- ok
- pending
- not_found
title: OrderbookStatus
PriceLevelResponse:
properties:
price:
type: number
title: Price
qty:
type: number
title: Qty
breakdown:
additionalProperties:
type: number
type: object
title: Breakdown
type: object
required:
- price
- qty
title: PriceLevelResponse
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
OrderbookResponse:
properties:
status:
$ref: '#/components/schemas/OrderbookStatus'
orderbook:
anyOf:
- $ref: '#/components/schemas/OrderbookData'
- type: 'null'
message:
anyOf:
- type: string
- type: 'null'
title: Message
type: object
required:
- status
title: OrderbookResponse
OrderbookData:
properties:
river_id:
anyOf:
- type: integer
- type: 'null'
title: River Id
generic_asset_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Generic Asset Id
bids:
items:
$ref: '#/components/schemas/PriceLevelResponse'
type: array
title: Bids
asks:
items:
$ref: '#/components/schemas/PriceLevelResponse'
type: array
title: Asks
best_bid_price:
anyOf:
- type: number
- type: 'null'
title: Best Bid Price
best_ask_price:
anyOf:
- type: number
- type: 'null'
title: Best Ask Price
exchange_timestamp:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Exchange Timestamp
description: Exchange orderbook timestamp (UTC)
type: object
required:
- bids
- asks
title: OrderbookData
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
securitySchemes:
SignedRequestKeyId:
type: apiKey
in: header
name: X-River-Key-Id
description: UUID of your API key (from Settings → API Keys).
SignedRequestTimestamp:
type: apiKey
in: header
name: X-River-Timestamp
description: Current unix seconds. Must be within 30s of server time.
SignedRequestSignature:
type: apiKey
in: header
name: X-River-Signature
description: 'Base64 Ed25519 signature over the canonical request: METHOD\nPATH\nSORTED_QUERY\nTIMESTAMP\nSHA256(body) hex. See /api-reference/authentication for the full recipe.'
BearerAuth:
type: http
scheme: bearer
description: JWT bearer token for web client authentication.