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
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.
/users
Create sub-account
Full endpoint documentationcreateUsersCreates a new sub-account. Each sub-account has an isolated USD wallet, portfolio, and order book. The externalId is your own reference — it must be unique per partner and is used in all subsequent calls. Sub-accounts start with a walletBalance of $0 — fund them via POST /users/{userId}/deposit.
Parameters
This endpoint has no path or query parameters.
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
externalIdstringRequiredYour internal user identifier. Must be unique per partner.
Example: "usr_8821"
displayNamestringFull name shown in the MyStocks admin console.
Example: "Alice K."
emailstringEmail address for the sub-account user.
Example: "alice@yourapp.com"
phonestringCustomer phone number in international format when available.
Example: "+254712345678"
countrystringISO 3166-1 alpha-2 country code used for onboarding and compliance context.
Example: "KE"
Response fields
subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
displayNamestring | nullExample: "Alice K."
emailstring | nullExample: "alice@yourapp.com"
kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
kycReviewedAtstring | nullTimestamp of the rejection decision.
statusactive | frozen | closedwalletBalancenumberCurrent USD wallet balance.
Example: 1250
Response codes
201Sub-account created.409externalId already exists.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.
/auto-register
Auto-register sub-account
Full endpoint documentationcreateAutoRegisterSingle-call convenience endpoint that creates a sub-account and returns an API token for it. Idempotent on uid — calling twice with the same uid returns the existing sub-account and a fresh token. Equivalent to POST /users but returns auth context in one round trip.
Parameters
This endpoint has no path or query parameters.
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
uidstringRequiredYour internal user identifier. Idempotency key -- calling twice with the same uid returns the same sub-account.
Example: "user_42"
emailstringEmail address for the sub-account user.
Example: "alice@yourapp.com"
namestringFull name shown in the MyStocks admin console.
Example: "Alice K."
phonestringUser phone number in E.164 format.
Example: "+254712345678"
countrystringISO 3166-1 alpha-2 country code.
Example: "KE"
Response fields
subAccountIdstringtokenstringShort-lived auth token for the sub-account.
isNewbooleantrue if this call created the account, false if it already existed.
Response codes
200Sub-account created or retrieved./users
Create sub-account
Full endpoint documentationcreateUsersCreates a new sub-account. Each sub-account has an isolated USD wallet, portfolio, and order book. The externalId is your own reference — it must be unique per partner and is used in all subsequent calls. Sub-accounts start with a walletBalance of $0 — fund them via POST /users/{userId}/deposit.
Parameters
This endpoint has no path or query parameters.
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
externalIdstringRequiredYour internal user identifier. Must be unique per partner.
Example: "usr_8821"
displayNamestringFull name shown in the MyStocks admin console.
Example: "Alice K."
emailstringEmail address for the sub-account user.
Example: "alice@yourapp.com"
phonestringCustomer phone number in international format when available.
Example: "+254712345678"
countrystringISO 3166-1 alpha-2 country code used for onboarding and compliance context.
Example: "KE"
Response fields
subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
displayNamestring | nullExample: "Alice K."
emailstring | nullExample: "alice@yourapp.com"
kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
kycReviewedAtstring | nullTimestamp of the rejection decision.
statusactive | frozen | closedwalletBalancenumberCurrent USD wallet balance.
Example: 1250
Response codes
201Sub-account created.409externalId already exists.List & fetch
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.
/users
List sub-accounts
Full endpoint documentationlistUsersReturns all sub-accounts for your partner key. Supports pagination and status filtering. Status filters are applied before pagination. Follow nextCursor even for an empty filtered page with hasMore=true. Invalid or foreign cursors return 400.
Parameters
statusactive | frozen | closed | ALLquerycursorstringqueryOpaque cursor from nextCursor of the previous response. Omit for the first page.
limitintegerqueryExample: 100
externalIdstringqueryExact partner-owned external identifier lookup; combined with status when supplied.
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Response fields
accountsobject[]accounts[].subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
accounts[].userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
accounts[].externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
accounts[].displayNamestring | nullExample: "Alice K."
accounts[].emailstring | nullExample: "alice@yourapp.com"
accounts[].kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
accounts[].kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
accounts[].kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
accounts[].kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
accounts[].kycReviewedAtstring | nullTimestamp of the rejection decision.
accounts[].statusactive | frozen | closedaccounts[].walletBalancenumberCurrent USD wallet balance.
Example: 1250
usersobject[]Backward-compatible alias of accounts.
users[].subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
users[].userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
users[].externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
users[].displayNamestring | nullExample: "Alice K."
users[].emailstring | nullExample: "alice@yourapp.com"
users[].kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
users[].kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
users[].kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
users[].kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
users[].kycReviewedAtstring | nullTimestamp of the rejection decision.
users[].statusactive | frozen | closedusers[].walletBalancenumberCurrent USD wallet balance.
Example: 1250
countintegerhasMorebooleannextCursorstringResponse codes
200Sub-account list./users/{userId}
Get sub-account
Full endpoint documentationgetUsersReturns a single sub-account by its MyStocks subAccountId. Use this to check wallet balance, KYC status, and account state.
Parameters
userIdstringpathRequiredMyStocks sub-account ID (e.g. usr_abc123).
Example: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Response fields
subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
displayNamestring | nullExample: "Alice K."
emailstring | nullExample: "alice@yourapp.com"
kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
kycReviewedAtstring | nullTimestamp of the rejection decision.
statusactive | frozen | closedwalletBalancenumberCurrent USD wallet balance.
Example: 1250
Response codes
200Sub-account detail.404Sub-account not found.Update / freeze
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.
/users/{userId}
Update sub-account
Full endpoint documentationupdateUsersUpdates mutable fields on a sub-account. All fields are optional — only provided fields are changed. Set status to frozen to immediately suspend all trading, deposits, and withdrawals for a user. Set back to active to restore access.
Parameters
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
displayNamestringNew display name for the sub-account.
Example: "Alice Kamau"
emailstringNew email address.
statusactive | frozenfrozen suspends all trading, deposits and withdrawals. Set back to active to restore access.
Response fields
subAccountIdstringMyStocks internal ID. Use in /users/{userId}/... paths.
Example: "usr_abc123"
userIdstringCompatible alias of subAccountId. Use subAccountId as the canonical stored identifier.
externalIdstringYour own user identifier as set at creation.
Example: "usr_8821"
displayNamestring | nullExample: "Alice K."
emailstring | nullExample: "alice@yourapp.com"
kycStatusNONE | PENDING | VERIFIED | REJECTEDVERIFIED required for trading and asset subscriptions. REJECTED remains gated until re-asserted.
kycLevelNONE | BASIC | FULLBASIC = ID verified. FULL = enhanced due diligence.
kycRejectionCodestring | nullStable rejection code when kycStatus is REJECTED.
kycRejectionReasonstring | nullCustomer-safe remediation guidance when kycStatus is REJECTED.
kycReviewedAtstring | nullTimestamp of the rejection decision.
statusactive | frozen | closedwalletBalancenumberCurrent USD wallet balance.
Example: 1250
Response codes
200Updated sub-account.Close / offboard
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.
Registered webhooks receive account.closed after a successful closure.
Sandbox closure and identifiers
Use the returned subAccountId consistently. Sandbox routes also resolve your tenant-scoped externalId to that same account and wallet; they do not create a second wallet under the alias.
Frozen and closed Sandbox accounts reject deposits, withdrawals, trades, subscriptions and redemptions with HTTP 422 (ACCOUNT_NOT_ACTIVE). Closing is terminal: metadata updates or attempts to reopen a closed account return HTTP 409 (ACCOUNT_CLOSED). Create a new customer account for a new relationship. Cash transfers recheck account state inside the wallet transaction.
/users/{userId}
Close sub-account
Full endpoint documentationcloseUsersSoft-closes/offboards a sub-account. This is a terminal state: closed sub-accounts cannot trade, receive deposits, or withdraw. The record is retained for audit, regulatory, and support purposes. Closure is blocked while the sub-account has open orders, positive holdings, or unsettled proceeds. If the USD wallet has residual cash, either withdraw it first or pass residualCashHandling: "transfer_to_partner" to atomically move the cash back to the partner master wallet during closure. Emits account.closed to registered webhooks. Requires Idempotency-Key.
Parameters
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
residualCashHandlingrequire_zero | transfer_to_partnerHow to handle a remaining USD wallet balance. require_zero blocks closure if cash remains. transfer_to_partner atomically moves residual cash to the partner master wallet before closing.
reasonstringPartner-supplied closure reason stored for audit and support.
Example: "User requested account deletion in partner app."
Response fields
messagestringExample: "Sub-account closed. Residual cash transferred to partner master wallet."
subAccountIdstringExample: "usr_abc123"
externalIdstringExample: "user_42"
statusclosedresidualTransferUsdnumberUSD cash moved back to the partner master wallet during closure.
Example: 42.5
currencyUSDnewMasterBalancenumberPresent when residual cash was transferred.
Example: 10042.5
alreadyClosedbooleantrue when the sub-account was already closed.
Response codes
200Sub-account closed, or was already closed.409Closure blocked by open orders, positions, unsettled proceeds, or residual cash.Deposit
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.
/users/{userId}/deposit
Deposit to sub-account
Full endpoint documentationcreateDepositNative 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 | 2queryVersion 2 transfers actual currency without FX. Internal transfer only; body contains amount and currency.
Example: 1
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
amountnumberUSD 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
amountUsdnumberExplicit USD ledger amount. Use this when you also include local display fields.
Example: 500
currencystringCurrency of amount. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.
Example: "KES"
notestringOptional description shown in the transaction history.
Example: "Mpesa STK push ref KE2482"
localAmountnumberEquivalent amount in the user local currency. Stored for audit and compliance only.
Example: 65000
localCurrencystringISO 4217 code for the local amount.
Example: "KES"
fxRatenumberLegacy 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.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.
/users/{userId}/withdraw
Withdraw from sub-account
Full endpoint documentationcreateWithdrawNative 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 | 2queryVersion 2 transfers actual currency without FX. Internal transfer only; body contains amount and currency.
Example: 1
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
amountnumberUSD 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
amountUsdnumberExplicit USD ledger amount. Use this when you also include local cash-out display fields.
Example: 200
currencystringCurrency of amount. Defaults to USD. Non-USD amounts are converted with MyStocks managed FX.
Example: "KES"
notestringOptional description shown in transaction history.
localAmountnumberEquivalent in local currency for the audit trail.
Example: 26000
localCurrencystringISO 4217 code.
Example: "KES"
fxRatenumberLegacy 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.Assert 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.
Set status: "REJECTED", level: "NONE", and include both reasonCode and reason when your
provider declines the customer. Rejected customers remain blocked from trading and money movement.
This is a state-changing write, so a unique Idempotency-Key header is required (returns
400 MISSING_IDEMPOTENCY_KEY without it).
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.
/users/{userId}/kyc
KYC assertion
Full endpoint documentationcreateKycAsserts the KYC status of a sub-account. MyStocks defers identity verification to your platform — you send us the result of your own KYC process. VERIFIED + BASIC is sufficient to unlock trading and asset subscriptions. FULL is required for higher transaction limits and private credit deals. REJECTED keeps the account gated and requires reasonCode plus customer-safe remediation text in reason; the response and kyc.updated webhook include reviewedAt. The reference field stores your internal KYC session ID for audit correlation. You may also attach an optional structured compliance profile (ID document type, date of birth, nationality, tax residency, PEP/sanctions screening result, risk rating, consent timestamp, evidence reference). Sensitive identifiers — idNumber and taxId — are stored as a sha256 fingerprint plus last-4 only and are never returned in full. All profile fields are optional and additive.
Parameters
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
statusNONE | PENDING | VERIFIED | REJECTEDRequiredOverall KYC status of the user.
Example: "VERIFIED"
levelNONE | BASIC | FULLRequiredBASIC = government ID verified (sufficient for trading). FULL = address + enhanced due diligence.
Example: "BASIC"
providerstringName of the KYC provider that performed the verification (e.g. sumsub, smile_identity).
Example: "sumsub"
referencestringYour internal KYC session reference for audit correlation.
Example: "kyc_session_88721"
reasonCodestringRequired when status is REJECTED. Stable machine-readable rejection code.
Example: "DOCUMENT_EXPIRED"
reasonstringRequired when status is REJECTED. Human-readable remediation guidance safe to show to the customer.
Example: "Submit a current government-issued identity document."
idDocumentTypePASSPORT | NATIONAL_ID | DRIVERS_LICENSE | VOTERS_CARD | RESIDENCE_PERMIT | OTHERType of identity document verified during your KYC process.
Example: "NATIONAL_ID"
idNumberstringIdentity document number. Stored as a sha256 fingerprint plus the last 4 characters only — never persisted or returned in full. The response and webhook echo back idNumberLast4.
Example: "24681012"
dateOfBirthstringDate of birth in ISO YYYY-MM-DD format.
Example: "1990-04-17"
nationalitystringNationality as an ISO 3166-1 alpha-2 country code.
Example: "KE"
countryOfResidencestringCountry of residence as an ISO 3166-1 alpha-2 country code.
Example: "KE"
addressstringResidential address (free-form, up to 500 characters).
Example: "12 Kenyatta Ave, Nairobi"
taxResidencystringCountry of tax residency as an ISO 3166-1 alpha-2 country code.
Example: "KE"
taxIdstringTaxpayer identification number (TIN). Stored as a sha256 fingerprint plus the last 4 characters only — never persisted or returned in full. The response and webhook echo back taxIdLast4.
Example: "A012345678Z"
pepbooleanWhether the user is a politically exposed person.
Example: false
sanctionsResultCLEAR | FLAGGED | PENDINGResult of your sanctions/PEP screening.
Example: "CLEAR"
screeningProviderstringName of the sanctions/PEP screening provider.
Example: "comply_advantage"
riskRatingLOW | MEDIUM | HIGHRisk rating assigned to the user by your onboarding process.
Example: "LOW"
consentAtstringISO 8601 timestamp when the user granted consent for processing.
Example: "2026-07-11T09:30:00Z"
evidenceReferencestringYour reference/URL to the stored KYC evidence bundle for audit.
Example: "kyc_docs_88721"
employmentStatusEMPLOYED | SELF_EMPLOYED | UNEMPLOYED | STUDENT | RETIRED | OTHEREmployment status captured during onboarding.
Example: "EMPLOYED"
occupationstringUser occupation or role.
Example: "Software engineer"
employerNamestringEmployer or business name when available.
Example: "Acme Ltd"
sourceOfFundsunionSource of funds used for investing.
Example: "SALARY"
sourceOfWealthunionSource of accumulated wealth.
Example: "EMPLOYMENT_INCOME"
annualIncomeBandUNDER_10000 | 10000_50000 | 50000_100000 | 100000_250000 | 250000_1000000 | OVER_1000000Annual income band in USD-equivalent.
Example: "50000_100000"
netWorthBandUNDER_25000 | 25000_100000 | 100000_500000 | 500000_1000000 | 1000000_5000000 | OVER_5000000Net-worth band in USD-equivalent.
Example: "100000_500000"
investmentExperienceNONE | LIMITED | MODERATE | EXPERIENCED | PROFESSIONALUser's investing experience level.
Example: "LIMITED"
investmentObjectivesstring[]Investment objectives captured during suitability/appropriateness checks.
Example: ["GROWTH","RETIREMENT"]
riskToleranceLOW | MEDIUM | HIGHUser-declared or assessed risk tolerance.
Example: "MEDIUM"
suitabilityStatusNOT_ASSESSED | SUITABLE | UNSUITABLE | REVIEW_REQUIREDSuitability outcome assigned by your onboarding process.
Example: "SUITABLE"
appropriatenessStatusNOT_ASSESSED | SUITABLE | UNSUITABLE | REVIEW_REQUIREDAppropriateness outcome for execution-only brokerage products.
Example: "SUITABLE"
assessmentReferencestringYour suitability/appropriateness assessment reference.
Example: "suitability_88721"
usPersonbooleanFATCA US-person declaration.
Example: false
fatcaStatusNOT_US_PERSON | US_PERSON | EXEMPT | UNKNOWNFATCA classification asserted by your onboarding flow.
Example: "NOT_US_PERSON"
crsTaxResidenciesobject[]CRS tax residencies. Tax IDs are stored as sha256 fingerprint plus last4 only.
crsTaxResidencies[].countrystringRequiredExample: "KE"
crsTaxResidencies[].taxIdstringStored as fingerprint + last4 only.
Example: "A012345678Z"
crsTaxResidencies[].reasonNoTinstringReason no TIN was supplied, if applicable.
marketAccountReferencesobject[]Market-specific brokerage/CSD account references. Account numbers are stored as fingerprint plus last4 only.
marketAccountReferences[].marketstringRequiredExample: "NSE"
marketAccountReferences[].brokerstringExample: "MyStocks Securities"
marketAccountReferences[].brokerAccountNumberstringStored as fingerprint + last4 only.
marketAccountReferences[].csdNumberstringStored as fingerprint + last4 only.
Response fields
messagestringkycStatusNONE | PENDING | VERIFIED | REJECTEDkycLevelNONE | BASIC | FULLreasonCodestring | nullreasonstring | nullreviewedAtstring | nullprofileobjectEcho of the compliance fields asserted on this call (present only when a profile was supplied). Sensitive identifiers appear as idNumberLast4 / taxIdLast4 — never in full.
Response codes
200KYC status updated.Notification devices
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.
/users/{userId}/devices
List notification devices
Full endpoint documentationlistUserDevicesLists mobile/web push devices registered for a sub-account. Tokens are never returned; only tokenLast4 is exposed for support correlation.
Parameters
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Response fields
devicesobject[]devices[].deviceIdstringExample: "dev_6f9a4d2c1b0e8a7c3d5f9012"
devices[].platformios | android | webdevices[].providerfcm | apns | expo | webpushdevices[].tokenLast4stringLast 4 characters of the token for support correlation.
devices[].appIdstringdevices[].localestringdevices[].timezonestringdevices[].externalDeviceIdstringdevices[].tagsstring[]devices[].enabledbooleandevices[].statusactive | disabled | revokeddevices[].createdAtstringdevices[].updatedAtstringdevices[].lastSeenAtstringcountintegerResponse codes
200Registered notification devices./users/{userId}/devices
Register notification device
Full endpoint documentationregisterUserDeviceRegisters or updates a push-token record for your mobile/web app. MyStocks stores only a token hash and last4. This endpoint gives partners a durable device-token model for mobile notification fanout. Webhooks remain the server-side event source of truth; your backend should receive webhooks or SSE events and fan out user-visible notifications through APNs, FCM, Expo, or Web Push using the device records you maintain.
Parameters
userIdstringpathRequiredExample: usr_abc123
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
tokenstringRequiredMobile/web push token from APNs, FCM, Expo, or Web Push. Stored as sha256 hash plus last4 only.
Example: "ExpoPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
platformios | android | webRequiredExample: "ios"
providerfcm | apns | expo | webpushRequiredExample: "expo"
appIdstringPartner app bundle/package ID.
Example: "com.afritrade.app"
deviceIdstringOptional partner device identifier. Used to keep one stable MyStocks deviceId across token rotations.
Example: "afritrade-device-42"
localestringExample: "en-KE"
timezonestringExample: "Africa/Nairobi"
tagsstring[]Example: ["retail","beta"]
enabledbooleanResponse fields
See the response format and API Reference for this endpoint.
Response codes
200Existing device registration updated.201Device registered./users/{userId}/devices/{deviceId}
Revoke notification device
Full endpoint documentationrevokeUserDeviceSoft-revokes a registered push device. The record is retained for audit and support correlation.
Parameters
userIdstringpathRequiredExample: usr_abc123
deviceIdstringpathRequiredExample: dev_6f9a4d2c1b0e8a7c3d5f9012
Headers
AuthorizationstringRequiredBearer sk_sandbox_<your_key> for sandbox requests.
Idempotency-KeystringheaderRequiredUnique 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
Response fields
messagestringsubAccountIdstringexternalIdstringdeviceIdstringstatusstringExample: "revoked"
Response codes
200Device revoked.404Device not found.Was this page useful?
Your signal helps us tighten partner onboarding docs.
Last updated on