Skip to content
Partner API Docs

Trading

Place BUY and SELL orders for your master account or any sub-account via the two-step quote → trade flow. Covers the quoteId model, order types and time-in-force, order history, cancellation, the transaction ledger, and portfolio.

Place BUY and SELL orders on behalf of your master account or any sub-account. Trading is a two-step flow in both sandbox and production: fetch a quote, then place the order with that quoteId within 60 seconds. Sandbox orders fill instantly; live production market orders submitted during exchange hours target PENDING → COMPLETED / REJECTED within 5 minutes. Always use Idempotency-Key on trade calls.

Quote
GET /quote/{symbol}
PENDING
POST /trade — escrowed
COMPLETED
order.filled

The full state machine — including LIMIT/STOP, cancellation, and expiry — is in Order & Money Lifecycles.

Get a quote

GET /quote/{symbol}

Step 1 of every trade (sandbox and production). Returns the fee breakdown — gross value, base fee (0.75%), optional partner markup, total cost (BUY) or estimated proceeds (SELL), sufficient-funds check — and a quoteId that the subsequent /trade call requires. Quotes are single-use and expire after 60 seconds (quoteExpiresAt); a stale or reused quoteId returns 409 STALE_QUOTE, and a quoteId whose symbol/side/quantity does not match the order returns 409 QUOTE_ORDER_MISMATCH. Quotes are indicative, not binding: settlement executes at the market price, protected by a deviation band (default ±10% of the quoted price).

FieldTypeRequiredDescription
typestringYesBUY or SELL.
quantitynumberNoNumber of whole shares (positive integer) — the order placed against this quote must match it. For fractional investing use cashValue instead. Mutually exclusive with cashValue.
cashValuenumberNoUSD amount for cash-mode (fractional) investing. Mutually exclusive with quantity. The trade must use the SAME cashValue.
subAccountIdstringNoSub-account ID. Omit to quote against the master wallet. The quoteId is bound to this account context, symbol, side, and sizing mode.
curl "https://mystocks.africa/api/v1/partner/quote/SCOM.KE?type=BUY&quantity=500&subAccountId=usr_abc123" \
  -H "Authorization: Bearer pk_live_<key>"
{ "quoteId": "qt_9f2c81d4b7a3", "quoteExpiresAt": "2026-07-07T09:46:00Z", "quoteTtlSeconds": 60, "symbol": "SCOM.KE", "exchange": "NSE", "currency": "KES", "type": "BUY", "quantity": 500, "localPrice": 16.5, "usdPrice": 0.127306, "gross": 63.65, "baseFee": 0.48, "partnerMarkupFee": 0.0, "fee": 0.48, "totalCost": 64.13, "sufficientFunds": true, "feeRate": 0.75, "note": "Pass quoteId to POST /trade within 60 seconds. No order has been placed." }

Place a trade

POST /trade (master) · POST /users/{userId}/trade (sub-account)

Place a BUY or SELL order. BUY escrows the cost (gross + fee) from the wallet immediately; SELL checks that the sub-account holds the required units. The symbol accepts exchange-qualified (SCOM.KE) or bare ticker (SCOM).

FieldTypeRequiredDescription
symbolstringYesExchange-qualified ticker or unambiguous bare ticker.
typestringYesBUY or SELL.
quantityintegerYesWhole shares. Must be a positive integer.
quoteIdstringYesA fresh quoteId from GET /quote/{symbol} for the same account, symbol, side, and quantity. Single-use, expires 60s after issue. Required in sandbox and production.
clientOrderIdstringNoYour internal order reference (≤80 chars). Unique per partner — a safe retry/dedupe handle alongside Idempotency-Key.
timeInForcestringNoGTC (default), DAY (auto-cancelled after 24h), or GTD (auto-cancelled at expiresAt). Expiry refunds BUY escrow / releases SELL reservations and fires order.cancelled. IOC is not supported.
expiresAtstringNoISO-8601. Required when timeInForce is GTD.
stopLossnumberNoAuto-sell price floor (USD). Optional.
takeProfitnumberNoAuto-sell price ceiling (USD). Optional.
# Step 1 — quote (quoteId expires in 60s)
curl "https://mystocks.africa/api/v1/partner/quote/SCOM.KE?type=BUY&quantity=1000&subAccountId=usr_abc123" \
  -H "Authorization: Bearer pk_live_<key>"
 
# Step 2 — place the order with that quoteId
curl -X POST "https://mystocks.africa/api/v1/partner/users/usr_abc123/trade" \
  -H "Authorization: Bearer pk_live_<key>" \
  -H "Idempotency-Key: trade_user42_001" \
  -H "Content-Type: application/json" \
  -d '{"symbol":"SCOM.KE","type":"BUY","quantity":1000,"quoteId":"qt_9f2c81d4b7a3","clientOrderId":"my-ord-10001"}'
{ "status": "PENDING", "orderId": "ord_abc123", "quoteId": "qt_9f2c81d4b7a3", "clientOrderId": "my-ord-10001", "timeInForce": "GTC", "subAccountId": "usr_abc123", "type": "BUY", "symbol": "SCOM.KE", "quantity": 1000, "priceAtOrder": 16.5, "usdPriceAtOrder": 0.0126, "gross": 12.6, "fee": 0.09, "totalCost": 12.69, "note": "Order is pending. Funds reserved from sub-account wallet." }

Production returns PENDING with the reserved price as priceAtOrder (local) and usdPriceAtOrder. In sandbox the same call resolves instantly to FILLED and instead carries usdPrice, localPrice, currency, and the post-trade newSubBalance.

Sandbox cheat codes. Quantity 100 → instant auto-fill (COMPLETED). Quantity 999 → instant auto-reject (REJECTED). Use them to test your webhook handlers without waiting for the admin queue.

List & fetch orders

GET /orders · GET /orders/{orderId} · GET /users/{userId}/orders

List orders for the master account or a specific sub-account (cursor-paginated), or fetch a single order by ID to poll status. Order fields include status, rejectionCode, rejectionReason, fee, baseFee, partnerMarkupFee, and timestamps. Filter by ?status=PENDING, ?symbol=, or ?from=/?to= date range.

{ "orders": [{ "id": "ord_abc123", "symbol": "SCOM.KE", "type": "BUY", "status": "COMPLETED", "quantity": 1000, "priceAtOrder": 16.5, "totalAmount": 12.69, "feeAmount": 0.09, "currency": "USD", "rejectionCode": null, "settledAt": "2026-06-04T11:30:00Z", "createdAt": "2026-06-04T09:45:00Z" }], "count": 1, "hasMore": false, "nextCursor": null }

Cancel an order

DELETE /orders/{orderId} · DELETE /users/{userId}/orders/{orderId}

Cancel an order while it is still PENDING (market orders awaiting fill) or WORKING (resting limit/stop orders). BUY escrow is refunded atomically. Returns HTTP 409 once the order has reached a terminal state (COMPLETED, CANCELLED, or REJECTED). A successful cancellation fires an order.cancelled webhook.

Transaction ledger

GET /users/{userId}/transactions

Full wallet ledger for a sub-account — every credit and debit in chronological order: deposits, withdrawals, trade escrows (INVEST), trade proceeds (SELL), dividend distributions (DISTRIBUTION), fund redemptions (REDEEM), and fees. Cursor-paginated.

FieldTypeRequiredDescription
typestringNoDEPOSIT | WITHDRAWAL | INVEST | SELL | DISTRIBUTION | REDEEM | FEE | TRANSFER_IN | TRANSFER_OUT
fromdateNoOn or after this date (YYYY-MM-DD, UTC).
todateNoOn or before this date (YYYY-MM-DD, UTC).
cursorstringNoOpaque cursor from nextCursor. Omit for first page.
limitintegerNoPage size, default 50, max 200.
{ "transactions": [{ "id": "txn_abc123", "type": "DEPOSIT", "status": "COMPLETED", "direction": "CREDIT", "amount": 500, "currency": "USD", "reference": "dep_riven_user_42_1743152580", "createdAt": "2026-06-01T10:00:00Z" }, { "id": "txn_xyz789", "type": "INVEST", "status": "PENDING", "direction": "DEBIT", "amount": 64.13, "currency": "USD", "description": "BUY SCOM.KE x500", "reference": "ord_def456" }], "count": 2, "hasMore": false, "nextCursor": null }

Portfolio

GET /portfolio (master) · GET /users/{userId}/portfolio (sub-account)

Holdings with current market value, cost basis, unrealized P&L, and currency. Also includes fund and bond positions if the account holds subscriptions.

{ "walletBalance": 487.31, "totalValue": 512.31, "holdings": [{ "symbol": "SCOM.KE", "name": "Safaricom PLC", "quantity": 1000, "avgCostUsd": 0.01269, "currentPriceUsd": 0.0126, "marketValue": 12.6, "unrealizedPnl": -0.09, "currency": "KES" }] }

On this page