Skip to content
Partner API Docs

Sub-Accounts

Create and manage end-user sub-accounts — each with an isolated USD wallet, portfolio, and order history. Covers create/auto-register, list/fetch, freeze, deposit, withdraw, and the KYC assertion API with an optional structured compliance profile.

Each of your end-users gets an isolated sub-account with its own USD wallet, portfolio, and order history. All write operations (deposit, withdraw, trade) are scoped to a single sub-account. The master wallet is the funding source for sub-account deposits.

Examples use the production base. Swap /api/v1/partner for /api/sandbox/v1/partner (and an sk_sandbox_ key) to run against the sandbox.

Create a sub-account

POST /users

Try in API Tester

Create a sub-account for one of your end-users. Returns HTTP 201. Use externalId to map MyStocks sub-account IDs back to your own user table.

FieldTypeRequiredDescription
externalIdstringYesYour internal user ID. Must be unique per partner — the natural idempotency key.
displayNamestringNoFull name shown in the MyStocks admin console.
emailstringNoUser email address.
bash
curl -X POST "https://mystocks.africa/api/v1/partner/users" \
-H "Authorization: Bearer pk_live_<key>" \
-H "Content-Type: application/json" \
-d '{"externalId":"user_42","displayName":"Jane Doe","email":"jane@example.com"}'
{ "subAccountId": "usr_abc123", "externalId": "user_42", "displayName": "Jane Doe", "email": "jane@example.com", "kycStatus": "NONE", "kycLevel": "NONE", "status": "active", "walletBalance": 0 }

Auto-register

POST /auto-register

Idempotent sub-account creation — safe to call on every user login. If a sub-account already exists for the given uid it is returned unchanged (no duplicate). Same response shape as POST /users.

FieldTypeRequiredDescription
uidstringYesYour internal user identifier. Acts as the idempotency key.
emailstringNoEmail address.
namestringNoDisplay name.
phonestringNoPhone in E.164 format, e.g. +254712345678.
countrystringNoISO 3166-1 alpha-2 country code.

List & fetch

GET /users · GET /users/{userId}

List all sub-accounts (cursor-paginated, default 100/page, max 500) or fetch a single one by its subAccountId. Filter by ?externalId= to look up using your own ID.

Update / freeze

PATCH /users/{userId}

Update display name or email. Set status: "frozen" to suspend all trading, deposits, and withdrawals for the sub-account; set back to status: "active" to restore access.

curl -X PATCH "https://mystocks.africa/api/v1/partner/users/usr_abc123" \
  -H "Authorization: Bearer pk_live_<key>" \
  -H "Content-Type: application/json" \
  -d '{"status":"frozen"}'

Close / offboard

DELETE /users/{userId}

Soft-close a sub-account when your user leaves your product or asks to terminate their investing account. Closure is terminal: the sub-account is retained for audit and regulatory history, but it can no longer trade, receive deposits, or withdraw.

Closure is blocked if the account has open orders, positive holdings, or unsettled proceeds. If the USD wallet still has cash, either call /withdraw first or pass residualCashHandling: "transfer_to_partner" to move the remaining cash to your master wallet during closure.

FieldTypeRequiredDescription
residualCashHandlingstringNorequire_zero (default) | transfer_to_partner.
reasonstringNoClosure reason stored in the audit trail.
curl -X DELETE "https://mystocks.africa/api/v1/partner/users/usr_abc123" \
  -H "Authorization: Bearer pk_live_<key>" \
  -H "Idempotency-Key: close_user42_001" \
  -H "Content-Type: application/json" \
  -d '{"residualCashHandling":"transfer_to_partner","reason":"User requested closure"}'
{ "message": "Sub-account closed.", "subAccountId": "usr_abc123", "externalId": "user_42", "status": "closed", "residualTransferUsd": 0, "currency": "USD" }

Registered webhooks receive account.closed after a successful closure.

Deposit

POST /users/{userId}/deposit

Try in API Tester

Credit a sub-account wallet. Send a USD amount, or send a supported local-currency amount with currency and MyStocks will calculate the USD ledger credit using managed FX. Always set Idempotency-Key.

FieldTypeRequiredDescription
amountnumberYesAmount in currency; defaults to USD.
amountUsdnumberNoExact USD ledger amount for direct-USD integrations.
currencystringNoISO 4217 code for amount, e.g. KES, NGN, GHS.
fxRatenumberNoDeprecated legacy partner rate; omit for managed FX.
notestringNoReference string shown in transaction history.
bash
curl -X POST "https://mystocks.africa/api/v1/partner/users/usr_abc123/deposit" \
-H "Authorization: Bearer pk_live_<key>" \
-H "Idempotency-Key: dep_user42_001" \
-H "Content-Type: application/json" \
-d '{"amount":65000,"currency":"KES","note":"mpesa_QHJ29SK"}'
{ "message": "Deposit successful.", "subAccountId": "usr_abc123", "amount": 500, "currency": "USD", "newSubBalance": 500, "newMasterBalance": 99500 }

Withdraw

POST /users/{userId}/withdraw

Debit a sub-account wallet and return the USD ledger amount to your master wallet. For supported non-USD requests, MyStocks converts amount in currency; your payout rail then delivers the local currency to the user. The sub-account must have sufficient uninvested balance. Always set Idempotency-Key.

FieldTypeRequiredDescription
amountnumberYesAmount in currency; defaults to USD.
amountUsdnumberNoExact USD ledger amount for direct-USD integrations.
currencystringNoISO 4217 code for amount.
fxRatenumberNoDeprecated legacy partner rate; omit for managed FX.
notestringNoReference for transaction history.
{ "message": "Withdrawal successful.", "subAccountId": "usr_abc123", "amount": 200, "currency": "USD", "newSubBalance": 300, "newMasterBalance": 99700 }

Assert KYC

POST /users/{userId}/kyc

Assert the KYC result you obtained using your own provider. Set status: "VERIFIED" and level: "BASIC" to unlock trading; FULL unlocks enhanced limits and higher-value subscriptions. This is a state-changing write, so a unique Idempotency-Key header is required (returns 400 MISSING_IDEMPOTENCY_KEY without it).

FieldTypeRequiredDescription
statusstringYesNONE | PENDING | VERIFIED
levelstringYesNONE | BASIC (ID verified) | FULL (enhanced due diligence)
providerstringNoKYC provider that performed the verification (e.g. sumsub).
referencestringNoYour KYC session ID for audit correlation.

You may also attach an optional structured compliance profile — all fields below are optional and additive. The two sensitive identifiers, idNumber and taxId, are stored as a sha256 fingerprint plus the last 4 characters only; they are never persisted or returned in full. The response and the kyc.updated webhook echo back idNumberLast4 / taxIdLast4.

FieldTypeRequiredDescription
idDocumentTypestringNoPASSPORT | NATIONAL_ID | DRIVERS_LICENSE | VOTERS_CARD | RESIDENCE_PERMIT | OTHER
idNumberstringNoID document number. Stored as fingerprint + last4 only; never returned in full.
dateOfBirthstringNoISO date, YYYY-MM-DD.
nationalitystringNoISO 3166-1 alpha-2 country code (e.g. KE).
countryOfResidencestringNoISO 3166-1 alpha-2 country code.
addressstringNoResidential address, up to 500 characters.
taxResidencystringNoISO 3166-1 alpha-2 country code of tax residency.
taxIdstringNoTIN. Stored as fingerprint + last4 only; never returned in full.
pepbooleanNoPolitically exposed person flag.
sanctionsResultstringNoCLEAR | FLAGGED | PENDING — your sanctions/PEP screening result.
screeningProviderstringNoSanctions/PEP screening provider name.
riskRatingstringNoLOW | MEDIUM | HIGH.
consentAtstringNoISO 8601 timestamp of user consent.
evidenceReferencestringNoYour reference/URL to the stored KYC evidence bundle.
employmentStatusstringNoEMPLOYED | SELF_EMPLOYED | UNEMPLOYED | STUDENT | RETIRED | OTHER.
sourceOfFundsstring/arrayNoSALARY | BUSINESS_INCOME | INVESTMENTS | INHERITANCE | SAVINGS | PENSION | GIFT | OTHER.
sourceOfWealthstring/arrayNoBUSINESS_OWNERSHIP | EMPLOYMENT_INCOME | INVESTMENTS | INHERITANCE | PROPERTY | SAVINGS | PENSION | OTHER.
annualIncomeBandstringNoUSD-equivalent income band, e.g. 50000_100000.
netWorthBandstringNoUSD-equivalent net-worth band, e.g. 100000_500000.
investmentExperiencestringNoNONE | LIMITED | MODERATE | EXPERIENCED | PROFESSIONAL.
investmentObjectivesarrayNoCAPITAL_PRESERVATION | INCOME | GROWTH | SPECULATION | HEDGING | RETIREMENT | OTHER.
suitabilityStatusstringNoNOT_ASSESSED | SUITABLE | UNSUITABLE | REVIEW_REQUIRED.
appropriatenessStatusstringNoNOT_ASSESSED | SUITABLE | UNSUITABLE | REVIEW_REQUIRED.
fatcaStatusstringNoNOT_US_PERSON | US_PERSON | EXEMPT | UNKNOWN.
crsTaxResidenciesarrayNoArray of country/taxId declarations. Tax IDs are stored as fingerprint + last4 only.
marketAccountReferencesarrayNoMarket-specific broker/CSD references. Account numbers are stored as fingerprint + last4 only.
curl -X POST "https://mystocks.africa/api/v1/partner/users/usr_abc123/kyc" \
  -H "Authorization: Bearer pk_live_<key>" \
  -H "Idempotency-Key: kyc_usr_abc123_001" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "VERIFIED",
    "level": "BASIC",
    "provider": "sumsub",
    "reference": "kyc_session_88721",
    "idDocumentType": "NATIONAL_ID",
    "idNumber": "24681012",
    "dateOfBirth": "1990-04-17",
    "nationality": "KE",
    "taxResidency": "KE",
    "sanctionsResult": "CLEAR",
    "riskRating": "LOW",
    "consentAt": "2026-07-11T09:30:00Z"
  }'
{ "message": "KYC status updated.", "subAccountId": "usr_abc123", "kycStatus": "VERIFIED", "kycLevel": "BASIC", "profile": { "idDocumentType": "NATIONAL_ID", "idNumberLast4": "1012", "nationality": "KE", "taxResidency": "KE", "sanctionsResult": "CLEAR", "riskRating": "LOW" } }

Notification devices

GET /users/{userId}/devices · POST /users/{userId}/devices · DELETE /users/{userId}/devices/{deviceId}

Register mobile/web push tokens for a sub-account so your app can fan out user-visible notifications after receiving MyStocks webhooks or SSE events. MyStocks stores only a token hash plus tokenLast4; raw APNs/FCM/Expo/Web Push tokens are never returned.

FieldTypeRequiredDescription
tokenstringYesAPNs, FCM, Expo, or Web Push token. Stored as fingerprint + last4 only.
platformstringYesios | android | web.
providerstringYesapns | fcm | expo | webpush.
appIdstringNoBundle/package/app identifier.
deviceIdstringNoYour stable device identifier; keeps the same MyStocks deviceId across token rotations.
localestringNoLocale such as en-KE.
timezonestringNoIANA timezone such as Africa/Nairobi.
tagsarrayNoOptional partner tags for segmentation.
curl -X POST "https://mystocks.africa/api/v1/partner/users/usr_abc123/devices" \
  -H "Authorization: Bearer pk_live_<key>" \
  -H "Idempotency-Key: device_usr_abc123_001" \
  -H "Content-Type: application/json" \
  -d '{"token":"ExpoPushToken[xxxxxxxxxxxxxxxxxxxxxx]","platform":"ios","provider":"expo","appId":"com.afritrade.app","deviceId":"device-42"}'

Was this page useful?

Your signal helps us tighten partner onboarding docs.

Last updated on

On this page