Skip to content
MyStocks Developers
API v1

API v1

Current stable contract

Versioning policyRelease changelog
Sandbox console
Broker API

FX & Currency

MyStocks-managed conversion for supported non-USD funding and withdrawals, plus execution and dividend FX. All wallets remain denominated in USD.

MyStocks keeps every partner and sub-account wallet in USD. For supported currencies, you can send a local-currency funding or withdrawal amount and MyStocks converts it to the USD ledger amount using the managed FX table. Your integration still owns the payment rail that collects or disburses the local currency.

FlowWho handles FX?What you provideWhat MyStocks does
Funding (deposit)MyStocksLocal amount + ISO currencyConverts with managed FX and credits the USD wallet
Equity tradingMyStocks—Applies the execution rate and settles in the local market currency
DividendsMyStocks—Converts the declared dividend to USD and credits the sub-account wallet
WithdrawalMyStocksLocal amount + ISO currencyConverts with managed FX and debits the USD wallet; partner completes payout

Funding and withdrawal conversion

Send the local amount in amount and its ISO 4217 code in currency. The response records the USD ledger amount, rate, source, and timestamp. Bulk GET /fx/rates export is disabled in live and sandbox with 403 FX_RATE_EXPORT_DISABLED under the current daily provider license. Use an FX quote to obtain conversion terms.

The conversion metadata uses fxSource: "MYSTOCKS_MANAGED_FX". You may instead send amountUsd when the integration has already determined the exact USD ledger amount. The fxRate request field is deprecated and retained only for legacy USD-ledger integrations; when supplied, it is identified as PARTNER_PROVIDED_LEGACY. Do not use it for new integrations.

GET

/fx/rates

Bulk FX rate export disabled

Full endpoint documentation
Sandbox availablelistFxRates

Bulk rate export is disabled under the current daily provider license in live and sandbox. Use POST /fx/quote or POST /users/{userId}/fx/quote for permitted conversions.

Parameters

This endpoint has no path or query parameters.

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Response fields

See the response format and API Reference for this endpoint.

Response codes

401Unauthorized.
403FX_RATE_EXPORT_DISABLED. Bulk redistribution is not licensed.

Trading FX

When a BUY order executes on the NSE, MyStocks converts the USD amount to KES at the execution rate. The fillPrice in the order response is expressed in the stock's native currency (for example, KES for NSE). MyStocks debits the sub-account USD wallet using the applied rate.

FX risk. Sub-account wallets are denominated in USD. Exchange-rate movements between funding and trade execution are borne by the end-user. MyStocks does not offer hedging; ensure your product disclosures reflect this.

Are the returned currency views separate wallets?

No. The current walletContractVersion: 1 uses USD_LEDGER_WITH_INDICATIVE_EQUIVALENTS. wallet is the actual USD balance. Entries in wallets, buyingPower and withdrawable are converted display equivalents, labelled balanceKind: INDICATIVE_EQUIVALENT, indicative: true, spendable: false. Do not add these values or treat KES and NGN views as separate funds. USD 100 shown in three currencies remains one balance.

Sandbox returns the same indicative fields using managed rates where available. If display rates are unavailable, the actual USD balance remains readable and only a USD view is returned; no local rate is invented. Sandbox money and its settlement behavior remain virtual. Native multicurrency wallet and FX workflows require a separate explicit contract; the v1 local-currency deposit still converts into USD.

How do I read actual currency wallets?

Opt in with GET /users/{userId}/wallet?walletContract=2 or X-MyStocks-Wallet-Contract: 2; the same option is available on /account. The additive currencyWallets list reads actual existing wallet documents. The old wallet and indicative wallets fields retain their meaning. walletContractVersion: 2 uses NATIVE_CURRENCY_WALLETS.

Each actual wallet reports cashBalance, reserved, externallyReserved, totalCash, available, withdrawable, eligibility and conversion eligibility. Trading escrow already debited from cash is shown separately: USD cash 100 and external escrow 25 means total cash 125, available cash 100. Do not subtract the external escrow again. Live non-USD native funding, payouts and conversion remain disabled pending operational enablement; Sandbox explicit FX is available once virtual funds have traceable origin. A positive balance alone does not authorize an operation. Existing funded but ineligible wallets remain visible.

Residence-based zero-wallet provisioning already uses the main platform policy in both environments. It never copies USD display equivalents into local wallet balances. No production balance migration is performed by reading this endpoint. Wallet transaction history remains the account transactions endpoint, with the actual entry currency preserved.

How do I convert actual wallet funds?

Create an owned-customer quote with POST /users/{userId}/fx/quote and { "fromCurrency": "KES", "toCurrency": "USD", "amount": 65000 }. Supply a fresh Idempotency-Key for this logical quote. Review sourceAmount, targetAmount, feeAmount, feeCurrency, rateConvention and expiresAt. The shared platform fee policy applies; configured rates and fees are authoritative, not an invented real-time provider rate. A quote touches no balances. Fees round to the nearest source minor unit; target credits round down to two decimal places so splitting conversions cannot create value. Quotes expose roundingMode: DOWN and calculationVersion: SOURCE_FEE_TARGET_FLOOR_V2. Refresh any unconsumed quote issued before this calculation policy.

Confirm the returned quoteId with POST /users/{userId}/fx/convert and a separate stable Idempotency-Key. Retry that same quote and key after an uncertain network outcome. Expired/consumed quotes and changed idempotency payloads return 409; missing rates fail instead of assuming parity. The source debit, target credit, fee ledger, traceable funding-origin transfer, audit and notification outbox are committed together. Native conversion currently supports currencies represented by the shared two-decimal wallet swap engine; digital assets and incompatible precision are excluded.

Sandbox operates on isolated virtual wallets. Live conversion remains disabled until identity, partner entitlements and currency funding/payout/reconciliation capabilities are approved. Existing untraceable balances are not relabelled as approved funding origin. A completed conversion receipt does not prove downstream webhook delivery; inspect delivery independently.

Partner master wallets use POST /fx/quote and POST /fx/convert with the same bodies, expiry and idempotency rules. Their funds and provenance remain under the master account; customer conversion never credits the master wallet. Live master FX also requires an active approved partner organization.

How do I fund and return actual currency balances?

Use POST /users/{userId}/deposit?walletContract=2 to transfer actual currency from firm to customer, and POST /users/{userId}/withdraw?walletContract=2 to return it. Send only amount and currency, with a unique Idempotency-Key. These are internal transfers, not bank payouts. Without the opt-in, the legacy USD behavior is unchanged.

Sandbox journey: top up virtual firm USD, quote/confirm firm USD-to-KES FX, transfer actual KES to the customer, quote/confirm customer KES-to-USD FX, then trade from USD. Return unused settled funds to the firm. Top-ups record virtual funding origin; legacy untraced money is never silently relabelled.

Trades and subscriptions still use USD. Convert beforehand; KES does not automatically fund a USD trade. Missing managed rates are rejected. Keep journals and FX fees grouped by actual currency. internalTransferAvailable is separate from external withdrawable.

Live native transfers require operational acceptance; native external payouts remain unavailable. The approved legacy USD payout workflow continues.

POST

/users/{userId}/deposit

Deposit to sub-account

Full endpoint documentation
Sandbox availablecreateDeposit

Native opt-in: ?walletContract=2 transfers actual specified currency between firm and customer wallets. Body contains only amount and currency. Requires traceable funding origin and settled available funds. Live requires approved currency capabilities and partner policy. No FX or external bank payout occurs. The v2 response contains transferId, direction, currency, amount, sourceBalance, targetBalance, simulationOnly, and externalPayout=false. Legacy v1 behavior below remains unchanged. Credits USD from your master wallet into a sub-account's wallet. Your master wallet must have sufficient balance — check via GET /account. Managed FX model — pass a USD ledger amount (amount or amountUsd) or pass amount with a non-USD currency such as KES. MyStocks converts supported non-USD amounts into the USD ledger using the managed conversion terms from the FX quote endpoints; bulk rate export is disabled. Sandbox uses the same conversion model but virtual balances only. Missing FX rates and unsupported currencies are rejected. Pass Idempotency-Key to make deposit retries safe on network failure.

Parameters

walletContract1 | 2query

Version 2 transfers actual currency without FX. Internal transfer only; body contains amount and currency.

Example: 1

userIdstringpathRequired

Example: usr_abc123

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Idempotency-KeystringheaderRequired

Unique string that makes write calls safe to retry on network failure. Required for money movement, trading, KYC, subscriptions, and webhook creation. Deduplicated for 24 hours -- duplicate keys return the cached response. HTTP 409 if a concurrent request with the same key is still in progress.

Example: dep_user42_1743152580

Request body

amountnumber

USD ledger amount to deposit, unless currency is a non-USD ISO code. If currency is non-USD, amount is interpreted in that currency and converted to USD by MyStocks managed FX.

Example: 500

amountUsdnumber

Explicit USD ledger amount. Use this when you also include local display fields.

Example: 500

currencystring

Currency of amount. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.

Example: "KES"

notestring

Optional description shown in the transaction history.

Example: "Mpesa STK push ref KE2482"

localAmountnumber

Equivalent amount in the user local currency. Stored for audit and compliance only.

Example: 65000

localCurrencystring

ISO 4217 code for the local amount.

Example: "KES"

fxRatenumber

Legacy partner-provided exchange rate. Omit this to use MyStocks managed FX.

Example: 130

Response fields

See the response format and API Reference for this endpoint.

Response codes

200Deposit processed.
400Master wallet has insufficient balance.
409Idempotency conflict.
POST

/users/{userId}/withdraw

Withdraw from sub-account

Full endpoint documentation
Sandbox availablecreateWithdraw

Native opt-in: ?walletContract=2 transfers actual specified currency between firm and customer wallets. Body contains only amount and currency. Requires traceable funding origin and settled available funds. Live requires approved currency capabilities and partner policy. No FX or external bank payout occurs. The v2 response contains transferId, direction, currency, amount, sourceBalance, targetBalance, simulationOnly, and externalPayout=false. Legacy v1 behavior below remains unchanged. Moves USD from a sub-account's wallet back to your master wallet. Use this when your user requests a cash-out. The funds are immediately available in your master wallet. Pass a USD ledger amount or a supported local amount + currency; non-USD values are converted with MyStocks managed FX. Sandbox uses the same conversion model but virtual balances only. Missing FX rates and unsupported currencies are rejected. Pass Idempotency-Key to make withdrawals safe to retry.

Parameters

walletContract1 | 2query

Version 2 transfers actual currency without FX. Internal transfer only; body contains amount and currency.

Example: 1

userIdstringpathRequired

Example: usr_abc123

Headers

AuthorizationstringRequired

Bearer sk_sandbox_<your_key> for sandbox requests.

Idempotency-KeystringheaderRequired

Unique string that makes write calls safe to retry on network failure. Required for money movement, trading, KYC, subscriptions, and webhook creation. Deduplicated for 24 hours -- duplicate keys return the cached response. HTTP 409 if a concurrent request with the same key is still in progress.

Example: dep_user42_1743152580

Request body

amountnumber

USD ledger amount to move, unless currency is a non-USD ISO code. If currency is non-USD, amount is interpreted in that currency and converted to USD by MyStocks managed FX.

Example: 200

amountUsdnumber

Explicit USD ledger amount. Use this when you also include local cash-out display fields.

Example: 200

currencystring

Currency of amount. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.

Example: "KES"

notestring

Optional description shown in transaction history.

localAmountnumber

Equivalent in local currency for the audit trail.

Example: 26000

localCurrencystring

ISO 4217 code.

Example: "KES"

fxRatenumber

Legacy partner-provided exchange rate. Omit this to use MyStocks managed FX.

Example: 130

Response fields

See the response format and API Reference for this endpoint.

Response codes

200Withdrawal processed.
400Sub-account has insufficient wallet balance.

How does an administrator manage currency access?

In /admin/partners, open the organization workspace and its overview. The currency workspace shows firm/customer cash by currency, policy entitlements, operational blockers and notification dispatch status. Finance-ledger permission is needed to read it. Release-management permission, recent sign-in, required MFA, a reason and explicit confirmation are required to change Live currency policy. Decisions write an audit record and queue a notification.

The global platformConfig/partnerCurrencyCapabilities record must contain verified funding, FX, payout and reconciliation acceptance for each Live currency; a partner cannot enable a missing rail by choosing a currency. The partner-specific partnerCurrencyPolicies/{partnerId} controls currencies, fxEnabled and nativeTransfersEnabled. No Live pilot is enabled by this release. USD trading and the legacy payout path remain separate.

Currency events wallet.converted, wallet.transferred and wallet.policy.updated enter a durable outbox. The secured existing events cron dispatches them to the partner stream and registered webhooks. DISPATCHED means handoff to that engine, not confirmed recipient delivery. Repeated dispatch uses the same eventId; consumers must deduplicate. Failed HTTP deliveries follow the existing webhook retry process.

The hosted and Sandbox Firm Balance pages provide actual-wallet selection, fee/expiry preview, explicit confirmation and native internal transfers. Capabilities shown in the browser do not override server authorization.

Rate freshness and source

Rates use local currency units per USD, with USD fixed at 1. Daily observations expire after 36 hours. Missing or expired non-USD rates block new conversions; a 60-second quote expiry does not renew its underlying rate. Quotes and new receipts record fxRateVersion, rateAsOf, source and convention. Currency equivalents are indicative; a completed ledger conversion does not prove external provider execution or treasury backing.

Daily indicative rates: Rates By Exchange Rate API. Bulk rate redistribution is disabled. Do not use the deprecated bulk endpoint to populate your own rate service.

Was this page useful?

Your signal helps us tighten partner onboarding docs.

Send note

Last updated on