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
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.
| Field | Type | Required | Description |
|---|---|---|---|
| externalId | string | Yes | Your internal user ID. Must be unique per partner — the natural idempotency key. |
| displayName | string | No | Full name shown in the MyStocks admin console. |
| string | No | User email address. |
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.
| Field | Type | Required | Description |
|---|---|---|---|
| uid | string | Yes | Your internal user identifier. Acts as the idempotency key. |
| string | No | Email address. | |
| name | string | No | Display name. |
| phone | string | No | Phone in E.164 format, e.g. +254712345678. |
| country | string | No | ISO 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.
Deposit
POST /users/{userId}/deposit
Credit a sub-account wallet. The USD amount is deducted from your master wallet and added to the
sub-account. Pass localAmount, localCurrency, and fxRate for the audit trail — they do not affect
how much USD is credited. Always set Idempotency-Key.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | number | Yes | USD to credit. Deducted from your master wallet. |
| localAmount | number | No | Local-currency equivalent (audit trail only). |
| localCurrency | string | No | ISO 4217 code, e.g. KES, NGN, GHS. |
| fxRate | number | No | Local units per USD that you applied. |
| note | string | No | Reference string shown in transaction history. |
Withdraw
POST /users/{userId}/withdraw
Debit a sub-account wallet and return the funds to your master wallet. You are responsible for paying
the user in their local currency at the rate you applied. The sub-account must have sufficient
uninvested balance. Always set Idempotency-Key.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | number | Yes | USD to move to your master wallet. |
| localAmount | number | No | Local equivalent for audit trail. |
| localCurrency | string | No | ISO 4217 code. |
| fxRate | number | No | Rate you applied. |
| note | string | No | Reference for transaction history. |
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).
| Field | Type | Required | Description |
|---|---|---|---|
| status | string | Yes | NONE | PENDING | VERIFIED |
| level | string | Yes | NONE | BASIC (ID verified) | FULL (enhanced due diligence) |
| provider | string | No | KYC provider that performed the verification (e.g. sumsub). |
| reference | string | No | Your 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.
| Field | Type | Required | Description |
|---|---|---|---|
| idDocumentType | string | No | PASSPORT | NATIONAL_ID | DRIVERS_LICENSE | VOTERS_CARD | RESIDENCE_PERMIT | OTHER |
| idNumber | string | No | ID document number. Stored as fingerprint + last4 only; never returned in full. |
| dateOfBirth | string | No | ISO date, YYYY-MM-DD. |
| nationality | string | No | ISO 3166-1 alpha-2 country code (e.g. KE). |
| countryOfResidence | string | No | ISO 3166-1 alpha-2 country code. |
| address | string | No | Residential address, up to 500 characters. |
| taxResidency | string | No | ISO 3166-1 alpha-2 country code of tax residency. |
| taxId | string | No | TIN. Stored as fingerprint + last4 only; never returned in full. |
| pep | boolean | No | Politically exposed person flag. |
| sanctionsResult | string | No | CLEAR | FLAGGED | PENDING — your sanctions/PEP screening result. |
| screeningProvider | string | No | Sanctions/PEP screening provider name. |
| riskRating | string | No | LOW | MEDIUM | HIGH. |
| consentAt | string | No | ISO 8601 timestamp of user consent. |
| evidenceReference | string | No | Your reference/URL to the stored KYC evidence bundle. |