Flows

Trading Workflow

Execute a trade end to end: pick a pair, lock a quote, place the order, track it, and check the wallet balance.

Trading on Sovera is quote-driven: you lock a price first, then place an order against it. This guide follows that sequence end to end, so the price you see is the price you trade at.

Overview

A trade always starts with a quote. The quote fixes the price and side for a few seconds; the order then executes that exact quote. Most orders settle on execution and come back COMPLETED. Read the order back by id to track it, and read the wallet balance to see the funds move.

  1. Pick a pair
  2. Request a quote
  3. Place an order against the quote
  4. Track the order
  5. Check the wallet balance

Prerequisites:

  • Your Owner API Key (x-api-key) or the user's token (Authorization: Bearer)
  • An active user with a wallet
  • Enough balance to cover the trade and its fee

Every response uses the standard envelope. Amounts are decimal strings and pairs are BASE/QUOTE.

Step-by-Step Guide

Step 1: Pick a Pair

List the pairs you can quote. Send the pair value as-is in the next step.

GEThttps://api.sandbox.sovera.io/sovx/v1/currencies/pairs
curl -G "https://api.sandbox.sovera.io/sovx/v1/currencies/pairs" \
  -H "Authorization: Bearer YOUR_USER_TOKEN" \
  --data-urlencode "filter[limit]=100"

Response:

{
  "success": true,
  "data": [
    {
      "object": "currency_pair",
      "pair": "BTC/USD",
      "base_currency": "BTC",
      "quote_currency": "USD",
      "is_trading_enabled": true
    }
  ],
  "meta": {
    "timestamp": "2026-10-12T14:29:58Z",
    "version": "v1",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
    "pagination": {
      "records": { "limit": 100, "has_next": false, "has_previous": false },
      "navigation": { "next": null, "previous": null }
    }
  }
}

See List Trading Pairs for full details.

Step 2: Request a Quote

Lock a price before you commit. A quote pins the pair, side, and size to one price, so the order in the next step executes at a known rate. Send exactly one of quantity (base currency) or amount (quote currency), as a decimal string. Add an x-idempotency-key so a retried request doesn't create a second quote.

POSThttps://api.sandbox.sovera.io/sovx/v1/users/:wallet_id/trading/quotes
curl -X POST "https://api.sandbox.sovera.io/sovx/v1/users/90f904da-dc14-535e-b2db-56be50db02c8/trading/quotes" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_USER_TOKEN" \
  -H "x-idempotency-key: 6f1c2a7e-9b3d-4f5a-8c2e-1d7b9a0e4c33" \
  -d '{
    "pair": "BTC/USD",
    "side": "BUY",
    "quantity": "0.01000000"
  }'

Response:

{
  "success": true,
  "data": {
    "id": "be27cd3f-3276-523e-a48c-f0661a079e72",
    "quote_id": "be27cd3f-3276-523e-a48c-f0661a079e72",
    "object": "quote",
    "status": "OPEN",
    "wallet_id": "90f904da-dc14-535e-b2db-56be50db02c8",
    "user_id": "43ac9e63-96c4-5f50-b788-dfbc6b265375",
    "pair": "BTC/USD",
    "base_currency": "BTC",
    "quote_currency": "USD",
    "side": "BUY",
    "quantity": "0.01000000",
    "amount": "450.0000",
    "buy_price": "45000.0000",
    "sell_price": null,
    "fee": "4.5000",
    "idempotency_key": "6f1c2a7e-9b3d-4f5a-8c2e-1d7b9a0e4c33",
    "created_at": "2026-10-12T14:30:00Z",
    "expires_at": "2026-10-12T14:30:15Z"
  },
  "meta": {
    "timestamp": "2026-10-12T14:30:00Z",
    "version": "v1",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

A quote is executable only until expires_at, usually a few seconds after created_at. Place the order right away or request a new quote.

See Request Quote for full details.

Step 3: Place the Order

Execute the quote. Pass its id as quote_id; the order takes the pair and side from the quote. Use a new x-idempotency-key: if the call times out, repeat it with the same key and you'll get the original order back instead of a second trade.

POSThttps://api.sandbox.sovera.io/sovx/v1/users/:wallet_id/trading/orders
curl -X POST "https://api.sandbox.sovera.io/sovx/v1/users/90f904da-dc14-535e-b2db-56be50db02c8/trading/orders" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_USER_TOKEN" \
  -H "x-idempotency-key: 9a7c3e1f-5b2d-4c8e-a6f0-1d3b5e7f9a2c" \
  -d '{
    "quote_id": "be27cd3f-3276-523e-a48c-f0661a079e72"
  }'

Response (201 Created):

{
  "success": true,
  "data": {
    "id": "168fc813-35b8-5447-89ed-07ec366f33c6",
    "order_id": "168fc813-35b8-5447-89ed-07ec366f33c6",
    "object": "order",
    "status": "COMPLETED",
    "status_reason": null,
    "quote_id": "be27cd3f-3276-523e-a48c-f0661a079e72",
    "transaction_id": "be2fa389-4b35-5c80-a830-2e849e9df31d",
    "user_id": "43ac9e63-96c4-5f50-b788-dfbc6b265375",
    "wallet_id": "90f904da-dc14-535e-b2db-56be50db02c8",
    "customer_id": "corp-redundant-firewall",
    "pair": "BTC/USD",
    "base_currency": "BTC",
    "quote_currency": "USD",
    "side": "BUY",
    "quantity": "0.01000000",
    "price": "45000.0000",
    "amount": "450.0000",
    "fee": "4.5000",
    "tx_hash": null,
    "network": null,
    "idempotency_key": "9a7c3e1f-5b2d-4c8e-a6f0-1d3b5e7f9a2c",
    "created_at": "2026-10-12T14:30:04Z",
    "updated_at": "2026-10-12T14:30:05Z",
    "completed_at": "2026-10-12T14:30:05Z",
    "created_by": "43ac9e63-96c4-5f50-b788-dfbc6b265375",
    "updated_by": "43ac9e63-96c4-5f50-b788-dfbc6b265375"
  },
  "meta": {
    "timestamp": "2026-10-12T14:30:05Z",
    "version": "v1",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

Read status to decide what's next:

  • COMPLETED—the trade settled. Go to Step 5.
  • PENDING or PROCESSING—accepted but not settled. Go to Step 4.
  • FAILED—status_reason.code says why. QUOTE_REJECTED means the quote was rejected at execution and is now used up: request a new quote.

Save the order id. transaction_id links the trade to its row in the wallet's transaction history.

See Place Order for full details.

Step 4: Track the Order

Read the order by id, or list the wallet's open orders with a status filter.

GEThttps://api.sandbox.sovera.io/sovx/v1/users/:wallet_id/trading/orders/:order_id
curl -X GET "https://api.sandbox.sovera.io/sovx/v1/users/90f904da-dc14-535e-b2db-56be50db02c8/trading/orders/168fc813-35b8-5447-89ed-07ec366f33c6" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"
curl -G "https://api.sandbox.sovera.io/sovx/v1/users/90f904da-dc14-535e-b2db-56be50db02c8/trading/orders" \
  -H "Authorization: Bearer YOUR_USER_TOKEN" \
  --data-urlencode "filter[where][status][inq][]=PENDING" \
  --data-urlencode "filter[where][status][inq][]=PROCESSING"

Both return the same order object as Step 3. See Get Order and List Orders.

Step 5: Check the Wallet Balance

Confirm the outcome by reading the wallet balance. After a buy you'll see more of the bought asset and less of what you spent; a sell is the reverse.

GEThttps://api.sandbox.sovera.io/sovx/v1/wallets/:wallet_id/balance
curl -X GET "https://api.sandbox.sovera.io/sovx/v1/wallets/90f904da-dc14-535e-b2db-56be50db02c8/balance" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"

Response:

{
  "success": true,
  "data": {
    "object": "wallet_balance",
    "wallet_id": "90f904da-dc14-535e-b2db-56be50db02c8",
    "user_id": "43ac9e63-96c4-5f50-b788-dfbc6b265375",
    "balances": [
      {
        "currency": "BTC",
        "total": "0.01000000",
        "available": "0.01000000",
        "held": "0.00000000"
      },
      {
        "currency": "USD",
        "total": "9545.5000",
        "available": "9545.5000",
        "held": "0.0000"
      }
    ],
    "as_of": "2026-10-12T14:31:00Z"
  },
  "meta": {
    "timestamp": "2026-10-12T14:31:00Z",
    "version": "v1",
    "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

See Wallets for full details.

Troubleshooting

Quote Expired

Error: 400 QUOTE_EXPIRED, with source.pointer /quote_id. Solution: Request a new quote and place the order before its expires_at.

Quote Already Executed

Error: 422 INVALID_STATE_TRANSITION, with source.pointer /quote_id. Solution: Each quote executes once. If you were retrying after a timeout, repeat the original request with the same x-idempotency-key to get the existing order, or read it with List Orders filtered by idempotency_key.

Order Failed

Issue: The order came back 201 with status: "FAILED" and status_reason.code: "QUOTE_REJECTED". Solution: The quote was rejected at execution and is used up. Request a new quote.

Invalid Request

Error: 422 VALIDATION_ERROR. source.pointer names the field, e.g. /pair for a pair that isn't BASE/QUOTE, or /amount when you send both quantity and amount. Solution: Fix the field and retry. Check the pair against List Trading Pairs.

Wallet or Quote Not Found

Error: 404 WALLET_NOT_FOUND or 404 QUOTE_NOT_FOUND. Solution: Use a wallet that belongs to your customer (and to the user, with a user token), and a quote requested for that same wallet.

Best Practices

  1. Quote right before you trade—quotes live for seconds; expires_at is the authority.
  2. Check the balance first—confirm the wallet covers the trade and its fee.
  3. Send an idempotency key on every POST—a retry returns the original result instead of trading twice.
  4. Store the order id—use it to track the order and reconcile it with the wallet's history.
  5. Read status, not the HTTP code—a rejected quote still returns 201 with a FAILED order.

Next Steps

Support

Need help? Email [email protected]

On this page