Executing a Trade

End-to-end walkthrough for executing a trade on behalf of a client — list asset pairs, calculate a quote, execute it, and retrieve the resulting order.

This guide walks you through running a single trade end-to-end — from discovering the right asset pair, to locking in a price with a quote, to executing the trade and retrieving the final order record. By the end, you will have a completed trade order on behalf of a funded client and the pattern you need to run trades at scale.

For the concepts behind the two-step quote-and-execute model, asset pairs, and the token lifecycle, see the Trading guide.


What You Will Have at the End

  • A completed trade order with type: buy or type: sell and status: completed
  • A client whose asset balances have been updated to reflect the trade
  • The partner_order_ref you tagged on the trade (if provided), ready for reconciliation
  • A working pattern for calculating and executing quotes at scale

Core Rules

Four rules to keep in mind before you run anything.

Quote → Execute — execute within lifetime

A quote must be executed within the lifetime (in seconds) returned in the calculate response, using the token also returned there. If the quote expires, execution fails with 4008 and you must calculate a fresh quote. Tokens are single-use — once a token executes successfully, it cannot be reused.

Trade execution is synchronous

A successful execute response means the order has been placed and is returned in a final status — typically completed. No polling is required. The client's balances are updated on the response.

📘

The order response is the source of truth

For the final trade result — execution price, amounts, fees — always rely on the order object returned by execute (or retrieved later). Do not recalculate values from the quote — use the order fields as authoritative.

⚠️

If partner_order_ref is provided, it must be unique

partner_order_ref is optional on trade execution, but if you provide it, the value must be unique across all orders under your partner account. Reusing a value returns 2020 (HTTP 409). Generate a fresh reference per trade.


Before You Start

You need the following in place before running any of the calls below.

Prerequisites checklist

  • You have a verified, active client (status: verified, active: true). If not, complete the Onboarding a Client walkthrough first
  • The client has sufficient balance in the asset they will spend — fund them first via the Funding & Reconciliation walkthrough if needed
  • You have your Partner ID and can generate request signatures — see the Authentication guide
  • You know which asset pair the client wants to trade (e.g. BTC-USD) — or you will discover it in Step 1

Environment and signing

ItemDetails
EnvironmentProduction: https://external-api.coinmena.com
Sandbox: contact [email protected] for credentials
Signed headersAll signed requests need X-Partner-ID, X-Timestamp, and X-Signature. See the Authentication guide to generate them
📘

Placeholders in the cURL examples

Replace your_partner_id with your actual Partner ID, and <your_generated_signature> with the Base64 signature you compute per the Authentication guide. Every request below needs its own fresh timestamp and signature.


The Flow at a Glance

  1. List asset pairs → find the pair to trade and confirm it is tradable
  2. Calculate a quote → receive a locked price and a short-lived token
  3. Execute the quote → place the order using the token before lifetime expires
  4. Retrieve the order (optional) → fetch by id or partner_order_ref for reconciliation

Step 1 — List Asset Pairs

Fetch the list of tradeable asset pairs to pick the one you want and to confirm it is currently tradeable.

curl -X GET "https://external-api.coinmena.com/v1/partner/asset-pairs" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654321000" \
  -H "X-Signature: <your_generated_signature>"

Response — 200 OK (trimmed)

{
  "items": [
    {
      "id": "BTC-USD",
      "type": "crypto",
      "base_id": "BTC",
      "quote_id": "USD",
      "decimal_places": 2,
      "inv_decimal_places": 8,
      "min_volume": "0.0001",
      "max_volume": "100",
      "trading_fee": "0.5",
      "tradable": true,
      "buy": "67550.75",
      "sell": "67450.25",
      "daily_change_pct": "2.35"
    }
  ]
}

What to Read

  • id — the pair identifier you will submit in Step 2 (e.g. BTC-USD)
  • tradable — must be true. If false, quote calculation fails with 4000
  • min_volume / max_volume — your base_amount must fall within these bounds or quote calculation fails with 4003 / 4004
  • buy / sell — current prices, useful for showing the client before calculating a quote
  • inv_decimal_places / decimal_places — precision for base and quote amounts respectively (for BTC-USD above, base amounts allow up to 8 decimals, quote amounts allow up to 2)
📘

Asset pair format

Pair IDs follow BASE-QUOTE format. For BTC-USD: BTC is the base asset (what is being bought or sold), USD is the quote asset (what it is priced in). See the Trading guide for the full breakdown.


Step 2 — Calculate a Quote

Calculate a quote for the trade. Submit the client, pair, side, and an amount — either base_amount (amount in the base asset, e.g. BTC) or quote_amount (amount in the quote asset, e.g. USD).

curl -X POST "https://external-api.coinmena.com/v1/partner/quotes" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654322000" \
  -H "X-Signature: <your_generated_signature>" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_client_id": "user_12345",
    "asset_pair": "BTC-USD",
    "side": "buy",
    "base_amount": "0.001"
  }'

Response — 200 OK

{
  "result": {
    "market_price": "67500.50",
    "base_amount": "0.00100000",
    "quote_amount": "67.50",
    "fee_amount": "0.34",
    "vat_amount": "0.00",
    "vat_percentage": null,
    "lifetime": 15,
    "token": "eyJhbGciOiJIUzI1NiJ9..."
  }
}

What to Persist

FieldWhy You Need It
tokenThe JWT you must send to execute. Bound to this quote, single-use
lifetimeSeconds the token is valid. Start your execute call before this elapses
market_priceThe locked price for this quote. Useful to display to the client
base_amountFinal base asset amount for the trade
quote_amountFinal quote asset amount for the trade
fee_amountFee the client will be charged
⚠️

base_amount or quote_amount — pick one

Submit exactly one of the two. Use base_amount when the client wants a specific quantity of the base asset (e.g. "buy 0.001 BTC"). Use quote_amount when the client wants to spend or receive a specific amount of the quote asset (e.g. "buy $100 of BTC"). Sending both, or neither, returns a validation error.

📘

Show the client before you execute

The quote response contains everything needed to display the final price and fees to the client. Most integrations show these numbers on a confirmation screen, then trigger execute when the client confirms — making sure the total time from quote calculation to execute is less than lifetime seconds.


Step 3 — Execute the Quote

Place the order using the token from Step 2. partner_order_ref is optional but recommended — it lets you reconcile this trade by your own reference later.

curl -X POST "https://external-api.coinmena.com/v1/partner/quotes/execute" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654323000" \
  -H "X-Signature: <your_generated_signature>" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_client_id": "user_12345",
    "token": "eyJhbGciOiJIUzI1NiJ9...",
    "partner_order_ref": "trade-abc-789"
  }'

Response — 200 OK

{
  "result": {
    "id": 67892,
    "order_no": "CRB0000067892",
    "partner_client_id": "user_12345",
    "type": "buy",
    "source": "market",
    "status": "completed",
    "price": "67500.50",
    "credit": "67.50",
    "credit_asset_id": "USD",
    "debit": "0.00100000",
    "debit_asset_id": "BTC",
    "fee": "0.34",
    "fee_asset_id": "USD",
    "vat": "0.00",
    "partner_order_ref": "trade-abc-789",
    "created_at": "2024-06-01T10:10:00Z",
    "transitioned_at": "2024-06-01T10:10:00Z",
    "updated_at": "2024-06-01T10:10:00Z"
  }
}

What to Persist

FieldWhy You Need It
idCoinMENA's order ID
partner_order_refYour own reference (if you provided one). Primary lookup key for reconciliation
statusShould be completed. If failed or rejected, log and investigate
priceExecution price — the locked price the trade was executed at
credit / debitSee Reading the order on a trade
fee / fee_asset_idFee charged for this trade, in the asset specified
created_atFor audit trails

Reading the Order on a Trade

On trade orders, credit and debit tell you what the client gave and received:

Order typedebit holdscredit holds
buyBase asset the client receives (e.g. 0.001 BTC)Quote asset the client pays (e.g. 67.50 USD)
sellQuote asset the client receives (e.g. 82.11 USD)Base asset the client gives (e.g. 0.0011 BTC)

In the example above, the client paid 67.50 USD (credit) and received 0.001 BTC (debit) on a buy. See the Orders guide for the full mapping.


Step 4 — Retrieve the Order (Optional)

If you need to look up the order again later — for reconciliation, support, or your own reporting — use either the id from Step 3 or the partner_order_ref you submitted.

By partner_order_ref (recommended)

curl -X GET "https://external-api.coinmena.com/v1/partner/orders/ref/trade-abc-789" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654324000" \
  -H "X-Signature: <your_generated_signature>"

By id

curl -X GET "https://external-api.coinmena.com/v1/partner/orders/67892" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654325000" \
  -H "X-Signature: <your_generated_signature>"

Both return the same order object shape as in Step 3.

📘

Use partner_order_ref if you provided one

Looking up by partner_order_ref means your own system stays the source of record — no mapping table needed between your IDs and CoinMENA's numeric id. For bulk reconciliation patterns, see the Funding & Reconciliation walkthrough.


What Can Go Wrong

The most common errors during trade execution and what to do about them. A failed quote or execute never applies any balance change — retry logic can assume the client's state is unchanged on any error response.

ErrorCodeLikely CauseFix
Invalid client ID2008partner_client_id not found under your partner accountVerify the client was onboarded and the reference ID matches
Client not yet verified2009Client status is not verifiedComplete onboarding — see the Onboarding a Client guide
Client is deactivated2010Client active is falseReactivate via PUT /v1/partner/clients/{partner_client_id}/activation
Invalid asset pair2013asset_pair does not exist or is not available to your partnerConfirm the pair ID is correct. Fetch GET /v1/partner/asset-pairs for valid IDs
Duplicate partner_order_ref2020You reused a reference valueFetch the existing order via the ref lookup. Retry with a new reference only if needed
Insufficient funds3005Client's balance in the asset they spend is less than the trade amountFund the client first — see Funding & Reconciliation
Trading is not available4000Pair is not tradableFilter pairs by tradable: true before quoting
Trading is temporarily unavailable4001Price feed is down for the pairRetry after a delay
Amount exceeds the maximum allowed4003base_amount is above the pair's max_volumeReduce the amount or split across multiple trades
Amount is below the minimum allowed4004base_amount is below the pair's min_volumeIncrease the amount
Trading token is invalid4008Token expired, was already used, or was issued for a different clientCalculate a fresh quote and use the new token
Unauthorized401Bad signature, expired timestamp, or IP not whitelistedWork through the Authentication troubleshooting checklist

For the complete list, see the Error Codes guide.


What’s Next
Next StepDescription
TradingConcept reference for asset pairs, quotes, and the token lifecycle
OrdersFull reference for the order object, statuses, and retrieval
Funding & ReconciliationReconcile trade orders alongside deposits and withdrawals
Error CodesFull reference for every error the API returns