Riv is in Early Access — help shape the financial control plane for AI agents. Send feedback
Riv.
Trading quickstart

Your first governed trading order

Four steps from an empty account to a perp order that passes through your own trading policies — with curl or any MCP client. Nothing here bypasses governance.

1

Create an agent and copy its riv_ key

In the app, create the agent that will trade. The riv_ key is shown once — put it in the Authorization header, or in RIV_API_KEY for MCP.

Open Agents →
.env
RIV_API_KEY=riv_…
Authorization: Bearer riv_…
2

Connect the venue

Connect the agent to Hyperliquid (testnet first). Riv checks the approved agent wallet and the builder-fee approval before the connection goes active.

Open Connections →
connection
venuehyperliquid
networktestnet
agent walletapproved ✓
builder feeapproved ✓
3

Create the first trading policy

Set the limits Riv enforces on every order — leverage, position size, assets, daily loss. With no policy in effect, every order is blocked (fail-closed).

Open Trading policies →
trading policies
max_leverageBTC · 3x→ block
max_position_size500 USDC→ block
order_notional_threshold100 USDC→ require_approval
4

Send the first governed order

Pick curl or MCP. The example is small on purpose: BTC, market, 12 USDC notional, 2x leverage.

curl -X POST https://riventa.dev/api/v1/trading/authorize \
  -H "Authorization: Bearer riv_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{"asset":"BTC","side":"buy","orderType":"market","notional":12,"leverage":2}'

Idempotency-Key is mandatory: reuse the same value to retry the same order; a new order needs a new value.

What comes back

Two independent levels: status is the governance decision, execution.status is what the venue did. approved means your policies allowed the order — only execution tells whether it filled.

200 · approved and filled

HTTP 200
{
"decision": "allow",
"reason": "within limits (2 policies evaluated)",
"activityId": "9b1de2c4-3f6a-4c8e-b2d7-5e1f0a4c9d21",
"executed": true,
"venueOrderId": "58126294403",
"status": "approved",
"execution": {
"status": "filled",
"venueOrderId": "58126294403",
"cloid": "0x9b1de2c43f6a4c8eb2d75e1f0a4c9d21",
"filledSz": "0.0001",
"avgPx": "112004.0",
"builderFee": "0.000112"
}
}

202 · waiting for human approval

HTTP 202
{
"decision": "require_approval",
"reason": "order_notional_threshold of 10 USDC exceeded (order of 12)",
"activityId": "4d7c2a19-8e5b-4f30-a6c1-0b9e7d2f5a83",
"executed": false,
"status": "pending"
}

422 · allowed by policy, refused by the venue

HTTP 422
{
"decision": "allow",
"reason": "within limits (2 policies evaluated)",
"reasonCode": "venue_error",
"activityId": "5cd2599f-22aa-44b9-a32d-4a284a91608e",
"executed": false,
"status": "approved",
"execution": {
"status": "failed",
"cloid": "0x5cd2599f22aa44b9a32d4a284a91608e",
"venueStatusRaw": "oracleRejected",
"venueReason": "order 0: Price too far from oracle asset=4"
},
"error": {
"code": "venue_rejected",
"message": "order 0: Price too far from oracle asset=4",
"retryable": false,
"hint": "Fix the order and send a new request with a new Idempotency-Key."
}
}
  • A blocked order is a normal 200 with status "blocked" and a reasonCode — the policy worked. Adjust the order and resubmit with a new Idempotency-Key.
  • Through MCP, place_order returns the same information as text: the decision, the reason, the execution outcome and the ledger activityId.

Ready to govern your agents' money?