Clients
A client is an end user you onboard onto CoinMENA under your partner account. This page explains the client object, its lifecycle, the KYC status model, and the rules that govern it.
Clients are the core object of the Partner API — every trade, deposit, withdrawal, and balance lookup is scoped to a client. This page explains what a client is, how it moves through its lifecycle, and the rules that govern it.
What Is a Client
A client is an end user of your product, registered under your partner account on CoinMENA. You create clients on their behalf using the Partner API, and from that point forward every trading or funding action is performed for a specific client.
Key points:
- Clients created through the Partner API are auto-verified — they skip the standard user-facing KYC flow and are immediately eligible to trade and be funded.
- Only personal accounts can be created through the API. Corporate accounts are onboarded through a separate, out-of-band process — contact [email protected] for corporate onboarding.
- Each client has two identifiers: CoinMENA's internal
idand your ownpartner_client_id. You use yourpartner_client_idacross every client-scoped endpoint.
Client Lifecycle at a Glance
A typical client moves through these stages:
- Create — You submit the client's personal, address, employment, and compliance details. The client is created with
status: verifiedandactive: true. - Upload KYC documents — Upload the KYC documents required for the client as part of onboarding. Required document types depend on your setup with CoinMENA.
- Fund and trade — The client can now be funded via deposits and can trade.
- Update over time — Client details can be updated. Some fields are immutable after creation (see Immutable Fields).
- Activate or deactivate — Temporarily disable a client's ability to trade or be funded without deleting the record.
Create vs Update Behavior
The create and update endpoints behave differently. Understand both before integrating:
| Behavior | POST /v1/partner/clients (Create) | PUT /v1/partner/clients/{partner_client_id}/ (Update) |
|---|---|---|
| What it does | Creates a new client record | Replaces an existing client's full record |
| Payload model | All required fields must be sent | Full replacement — all required fields must be sent on every update, not just the ones you are changing |
| Immutable fields | account_type, birth_date, place_of_birth_id are set at creation | The three immutable fields are not accepted in the update payload |
| Uniqueness check | partner_client_id, email, phone, government_id_number must be unique | Same uniqueness rules apply to updated values |
| On duplicate | Returns a duplicate-value error (2016, 2026, 2027, or 2028) | Same |
Update is not a patchThere is no partial update. If you send only the fields you want to change, the other required fields will fail validation. Always re-send the full client payload on every update, minus the three immutable fields.
Required Fields Summary
These fields must be provided when creating a client. There is no shorter minimum — the API will reject a create request missing any of them.
| Category | Required Fields |
|---|---|
| Identity | partner_client_id, account_type, name, birth_date |
| Contact | email, phone |
| Residency & legal | residency_id, citizenship_id, place_of_birth_id, government_id_number, government_id_doe |
| Address | address (with street, city, postal_code, unit — all required) |
| Employment | employment (with status required; other subfields conditional — see Employment) |
| Compliance | trading_volume, account_purpose |
On update, the same fields are required except partner_client_id, account_type, birth_date, and place_of_birth_id (the last three are immutable; partner_client_id is in the URL path).
Optional fields: national_number, residency_visa_number, residency_visa_doe. Residency visa fields become conditionally required — see Countries.
The Client Object
Every client endpoint returns the same client shape. Fields are grouped below for readability.
Identity
| Field | Type | Constraints | Description |
|---|---|---|---|
id | integer | — | CoinMENA's internal numeric ID for the client |
partner_client_id | string | max 255, unique | Your own reference ID. Used in every client-scoped endpoint path |
account_type | string | personal | corporate | Account type. Only personal is creatable via the API |
name | string (nullable) | max 255 | Client's full legal name |
birth_date | string (date) | YYYY-MM-DD, immutable | Date of birth |
status | string | enum — see KYC Status Model | Current verification status |
active | boolean | — | Whether the client can trade and be funded |
Contact
| Field | Type | Constraints | Description |
|---|---|---|---|
email | string | valid email format, unique | Client's email address |
phone | string | international format, e.g. +97312345678, unique | Client's phone number |
Residency & Legal
| Field | Type | Constraints | Description |
|---|---|---|---|
residency_id | string | country ID from /v1/public/countries | Country of residence |
citizenship_id | string | country ID from /v1/public/countries | Country of citizenship |
place_of_birth_id | string | country ID, immutable | Country of birth |
national_number | string (nullable) | max 255 | National ID number, where applicable |
government_id_number | string | max 255, unique | Government-issued ID number |
government_id_doe | string (date) | YYYY-MM-DD | Government ID expiry date |
residency_visa_number | string (nullable) | max 255 | Residency visa number, if applicable |
residency_visa_doe | string (nullable) | YYYY-MM-DD | Residency visa expiry date, if applicable |
Address
All address subfields are required strings, maximum 255 characters each.
| Field | Type | Description |
|---|---|---|
street | string | Street name and number |
city | string | City of residence |
postal_code | string | Postal or ZIP code |
unit | string | Apartment, suite, or unit number |
Employment
| Field | Type | Constraints | Description |
|---|---|---|---|
status | string | enum | employed, self_employed, unemployed, housewife, retired |
company | string (nullable) | max 255 | Employer or company name (conditional) |
job_industry | string (nullable) | max 255 | Industry sector (conditional) |
job_level | string (nullable) | max 255 | Seniority level (conditional) |
job_title | string (nullable) | max 255 | Job title (conditional) |
See Employment — Validation Rules for the conditional requirements.
Compliance Metadata
| Field | Type | Constraints | Description |
|---|---|---|---|
trading_volume | string | max 255 | Expected monthly trading volume bracket |
account_purpose | array of strings | min 1 item, enum | One or more stated purposes for the account |
Timestamps
| Field | Type | Description |
|---|---|---|
created_at | string (datetime) | When the client record was created |
verified_at | string (datetime, nullable) | When the client was verified. null if not yet verified |
Example
{
"id": 12345,
"partner_client_id": "user_12345",
"account_type": "personal",
"status": "verified",
"active": true,
"name": "John Doe",
"email": "[email protected]",
"phone": "+97312345678",
"residency_id": "bh",
"citizenship_id": "bh",
"place_of_birth_id": "bh",
"birth_date": "1990-01-15",
"government_id_number": "1234567890",
"government_id_doe": "2028-01-15",
"address": {
"street": "123 Main St",
"city": "Manama",
"postal_code": "12345",
"unit": "4A"
},
"employment": {
"status": "employed",
"company": "Acme Corp",
"job_industry": "information_technology_telco",
"job_level": "senior",
"job_title": "Software Engineer"
},
"trading_volume": "10001_25000",
"account_purpose": ["short_term_trading", "converting_crypto_to_fiat"],
"created_at": "2024-06-01T10:00:00Z",
"verified_at": "2024-06-01T10:00:00Z"
}Account Types
The API supports two account types, but only one is creatable through the Partner API:
| Account Type | Creatable via API | Notes |
|---|---|---|
personal | Yes | The standard end-user account. Created with POST /v1/partner/clients |
corporate | No | Onboarded through a separate process. Contact CoinMENA to set one up |
If you submit any value other than personal to the create endpoint, the request is rejected with a validation error.
Client Identity — Two IDs
Every client has two identifiers that serve different purposes:
| Identifier | Type | Owner | Used For |
|---|---|---|---|
id | integer | CoinMENA | Internal reference. Returned in responses but not used in request paths |
partner_client_id | string | You | Your own reference ID. Used in every client-scoped endpoint path and body |
Always usepartner_client_idin request pathsEvery client-scoped endpoint —
GET /v1/partner/clients/{partner_client_id}/,PUT /v1/partner/clients/{partner_client_id}/activation,GET /v1/partner/clients/{partner_client_id}/documents, etc. — takes yourpartner_client_idin the path, not CoinMENA's internalid. Storepartner_client_idin your system as the primary client reference.
Your partner_client_id must be unique across your partner account. Reusing an existing partner_client_id on create returns error 2026 — it does not return or overwrite the existing client. See the Error Codes guide.
KYC Status Model
The status field reflects a client's KYC verification state. Partner-created clients start at verified and stay there unless CoinMENA's compliance team changes the state.
| Status | Meaning |
|---|---|
verified | Client has passed verification and can trade and be funded |
unverified | Client has been created but has not completed verification |
under_review | Verification is in progress — compliance is reviewing the client |
partially_verified | Client has completed some but not all verification steps |
docs_under_review | Supporting documents are being reviewed |
not_supported | Client cannot be verified — typically due to an unsupported country or regulatory restriction |
under_age | Client is below the legal age required by the applicable jurisdiction |
rejected | Client failed verification and cannot be onboarded |
Partner-created clients are verified on creationClients created through
POST /v1/partner/clientsare assignedstatus: verifiedautomatically — they do not need to go through the standard user-facing KYC flow. The other statuses are included for completeness and may appear on client records managed by CoinMENA's compliance team. Partners cannot trigger status transitions through the API.
Active vs Inactive
The active flag controls whether a client can trade or be funded. It is independent of status:
active: true— Client can calculate quotes, execute trades, deposit, and withdraw.active: false— Client is blocked from all trading and funding actions. Requests return error2010— The client is deactivated.
Toggle a client's active state using PUT /v1/partner/clients/{partner_client_id}/activation with {"active": true} or {"active": false}.
Use deactivation to temporarily suspend a client — for example, while investigating suspicious activity or pausing access pending account review. Deactivation is reversible; it does not delete the client record.
Immutable Fields
Three fields cannot be changed after a client is created:
| Field | Why It Is Immutable |
|---|---|
account_type | Account type is set at creation and cannot be switched between personal and corporate |
birth_date | Fundamental identity attribute — changes are not permitted for compliance reasons |
place_of_birth_id | Fundamental identity attribute — changes are not permitted for compliance reasons |
These fields are not accepted in the PUT /v1/partner/clients/{partner_client_id}/ update payload. Submitting them will not update the record; CoinMENA's schema validation ignores immutable fields on update.
Get these right on creationBecause these fields are immutable, double-check them before calling
POST /v1/partner/clients. A wrongbirth_dateorplace_of_birth_idcannot be corrected through the API — contact [email protected] if a correction is needed.
Employment — Validation Rules
Employment is a required object on client creation and update. The fields you must (or must not) send depend on the status value:
| Status | Required Fields | Disallowed Fields |
|---|---|---|
employed | company, job_industry, job_level, job_title | — |
self_employed | company, job_industry, job_title | job_level |
unemployed | — | company, job_industry, job_level, job_title |
housewife | — | company, job_industry, job_level, job_title |
retired | — | company, job_industry, job_level, job_title |
status itself is required for every employment submission.
Some employment subfields accept any string but should be submitted using CoinMENA's recommended enum values to align with the compliance model:
job_industry— submit one of CoinMENA's recommended industry codes (e.g.information_technology_telco,banking_financial_services)job_level— submit one of CoinMENA's recommended seniority levels (e.g.senior,manager,director)
See the API Reference for the full list of recommended values.
Countries — Residency, Citizenship, Place of Birth
Every client has three country fields, and each must resolve to a valid country ID from CoinMENA's supported list.
Country ID formatCountry IDs are lowercase short codes returned by
GET /v1/public/countries— for examplebh(Bahrain),sa(Saudi Arabia),ae(United Arab Emirates). Always fetch the list from the public endpoint rather than hardcoding IDs — the supported list can change. Do not use uppercase ISO codes or 3-letter codes; they will be rejected.
| Field | Source | Immutable After Creation |
|---|---|---|
residency_id | GET /v1/public/countries | No |
citizenship_id | GET /v1/public/countries | No |
place_of_birth_id | GET /v1/public/countries | Yes |
If the country is not supported or unsupported for the given field, creation or update fails:
| Error Code | Trigger |
|---|---|
2022 | residency_id is not a supported country |
2023 | citizenship_id is not a supported country |
2024 | place_of_birth_id is not a supported country (creation only — field is immutable) |
Residency Visa
When the client's residency country differs from their citizenship country, you must also submit their residency visa details:
residency_visa_number— residency visa document numberresidency_visa_doe— residency visa expiry date (YYYY-MM-DD)
Missing visa details when required returns error 2025 — Residency visa number and expiry are required.
Trading Volume & Account Purpose
Two compliance-related fields are required on every client create and update:
trading_volume
trading_volumeA string representing the client's expected monthly trading volume. Although the field accepts any string, you should submit one of CoinMENA's recommended volume brackets to align with the compliance model — e.g. 1000_10000, 10001_25000, 25001_100000.
See the API Reference for the full list of recommended bracket values.
account_purpose
account_purposeAn array of one or more strings indicating what the client intends to use the account for. Accepted values:
| Value | Description |
|---|---|
short_term_trading | Short-term crypto trading |
long_term_investment | Long-term investment in digital assets |
converting_crypto_to_fiat | Converting existing crypto holdings to fiat |
cross_border_transactions | Sending or receiving cross-border payments |
At least one value must be provided. Multiple values are allowed.
Documents
KYC documents are uploaded per client and are managed through a separate set of endpoints. Documents are part of the client lifecycle — upload the document types required by your setup with CoinMENA for each client you onboard. See the Onboarding a Client guide for the end-to-end walkthrough.
Supported Document Types
| Type | Description |
|---|---|
government_id | Government-issued ID (passport, national ID) |
proof_of_address | Utility bill, bank statement, or similar |
residency_visa | Residency visa document |
source_of_funds | Documentation proving source of funds |
bank_statement | Official bank statement |
inquiry_report | Inquiry or background report |
Upload Rules
- Front file: required
- Back file: optional — include for two-sided documents like national IDs
- Accepted formats: JPEG, PNG, HEIC, HEIF, PDF
- Maximum size: 5 MB per file
Presigned URLs for Download
When you retrieve a document via GET /v1/partner/clients/{partner_client_id}/documents/{document_id}, the response contains presigned S3 URLs in front_file and back_file. These URLs:
- Are signed and scoped to the document
- Expire after 900 seconds (15 minutes)
- Should be fetched on-demand — do not store the URLs in your database
If a presigned URL expires, re-request the document metadata to get a fresh URL.
Filtering
List documents for a client with GET /v1/partner/clients/{partner_client_id}/documents. The type query parameter accepts multiple values, so you can narrow results to one or several document types in a single request.
Validation Errors (422)
When a create or update request fails schema validation, the API returns 422 with a msg_detail array listing every field-level failure:
{
"code": 422,
"msg": "Validation Error",
"msg_detail": [
{
"loc": ["body", "email"],
"msg": "Input should be a valid email",
"type": "email_parsing"
},
{
"loc": ["body", "address", "street"],
"msg": "Field required",
"type": "missing"
}
]
}Use the loc array to identify the exact field that failed — it reflects the nested path of the field in your request body. See the Error Codes guide for the full 422 reference.
Common Mistakes
The most frequent issues partners hit when creating or updating clients:
| Mistake | Fix |
|---|---|
| Submitting uppercase or 3-letter country codes | Use the lowercase short codes returned by GET /v1/public/countries (e.g. bh, not BH or BHR) |
| Treating update as a partial PATCH | Re-send the full payload on every update — missing required fields will fail validation |
Submitting account_type: corporate on create | Only personal is creatable via the API. Contact CoinMENA for corporate onboarding |
Sending account_type, birth_date, or place_of_birth_id on update | These fields are immutable — strip them from the update payload |
Reusing a partner_client_id for a different client | Each partner_client_id must be unique per partner. Duplicates return 2026 |
Missing residency_visa_number or residency_visa_doe when residency ≠ citizenship | Submit both fields when the client's residency and citizenship differ — missing them returns 2025 |
Sending job_level for a self_employed status | job_level is not allowed for self_employed — see the Employment validation table |
| Forgetting one of the four address subfields | street, city, postal_code, and unit are all required — missing any returns a 422 |
Using splits like first_name / last_name | The API uses a single name field with the client's full legal name |
Where Clients Are Used
The partner_client_id is the link between a client and every downstream operation. A client is required for:
- Balances — retrieve
partner_client_id-scoped asset balances - Quotes — calculate a trade quote on behalf of a client
- Orders — execute a quote and retrieve the resulting order
- Deposits — credit fiat to a client's account
- Withdrawals — debit fiat from a client's account
- Documents — upload and retrieve KYC documents for a client
If the client is deactivated (active: false) or not yet verified, these downstream operations return 2010 or 2009 respectively.
Related Error Codes
The most common client-related errors:
| Code | Trigger |
|---|---|
2008 | Invalid client ID — client not found under your partner account |
2009 | The client is not yet verified |
2010 | The client is deactivated |
2016 | Government ID is already being used by another approved client |
2021 | Client is under the legal age |
2022 | Invalid residency country |
2023 | Invalid citizenship country |
2024 | Invalid place of birth country |
2025 | Residency visa number and expiry are required |
2026 | A client with this partner_client_id already exists |
2027 | A client with this email already exists |
2028 | A client with this phone already exists |
See the Error Codes guide for the full reference, including HTTP status codes and affected endpoints.
Updated 1 day ago
| Next Step | Description |
|---|---|
| Trading | Asset pairs, quotes, and how to execute trades on behalf of a client |
| Funding | Deposit and withdraw fiat balances for your clients |
| Error Codes | Full reference for every error the API returns |
