Onboarding a Client

End-to-end walkthrough for onboarding a new client on CoinMENA — from creation to optional document upload and final verification. Follow the steps in order.

This guide walks you through onboarding a single client from scratch. By the end, you will have a verified, active client under your partner account with all required KYC documents on file, ready to be funded and to trade.

For the concepts behind the client object, KYC status, and validation rules, see the Clients guide.


What You Will Have at the End

  • A client record under your partner account with status: verified and active: true
  • A partner_client_id you control, used in every future call for this client
  • The required KYC documents uploaded and on file for the client
  • A client ready to be funded and to trade — no additional activation step required

Before You Start

You need the following in place before running any of the calls below.

Prerequisites checklist

  • You have your Partner ID (provided by CoinMENA after your account is created)
  • You have your Ed25519 private key, and your public key has already been shared with CoinMENA
  • You can generate request signatures using the method described in the Authentication guide
  • You know which country IDs are supported — fetch them from GET /v1/public/countries (IDs are lowercase short codes like bh, sa, ae)
  • You have the KYC documents ready for the client — your onboarding with CoinMENA defines which document types are required

Environment and signing

ItemDetails
EnvironmentProduction: https://external-api.coinmena.com
Sandbox: contact [email protected] for credentials
Signed headersAll signed requests need X-Partner-ID, X-Timestamp, and X-Signature. See the Authentication guide to generate them
📘

Placeholders in the cURL examples

Replace your_partner_id with your actual Partner ID, and <your_generated_signature> with the Base64 signature you compute per the Authentication guide. Every request below needs its own fresh timestamp and signature.


The Flow at a Glance

Onboarding is a three-step flow. Complete all three steps in order for every client.

  1. Create the client → receive the client record, persist your partner_client_id
  2. Upload the required KYC documents → upload every document type CoinMENA requires for this client
  3. Confirm the client is ready → fetch the client and verify status: verified + active: true

All onboarding operations are synchronous

Every step below returns a final response. There is no pending or async state — if the API returns a success response, the operation is complete and the change is effective immediately.


Step 1 — Create the Client

Submit the client's details to POST /v1/partner/clients. All required fields must be present — the API rejects partial payloads.

curl -X POST "https://external-api.coinmena.com/v1/partner/clients" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654321000" \
  -H "X-Signature: <your_generated_signature>" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_client_id": "user_12345",
    "account_type": "personal",
    "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"]
  }'

Response — 201 Created

{
  "result": {
    "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"
  }
}

What to Persist

Store these fields on your side — they are what you need for every future call involving this client:

FieldWhy You Need It
partner_client_idYour reference ID. Used in every client-scoped endpoint path
idCoinMENA's internal numeric ID. Useful as a secondary reference
statusShould be verified on creation. Re-check later if a flow depends on it
activeShould be true on creation. Toggle via the activation endpoint if needed
created_atUseful for your own audit and reporting
⚠️

Uniqueness rules — what makes creation fail

The following fields must be unique across your partner account. Reusing a value returns a specific error:

  • partner_client_id2026
  • email2027
  • phone2028
  • government_id_number2016

The API does not return the existing client on a duplicate — it rejects the request. If you suspect a duplicate was intentional, fetch the existing client via GET /v1/partner/clients/{partner_client_id}/.

⚠️

Three fields are immutable after creation

account_type, birth_date, and place_of_birth_id cannot be changed later. Double-check these values before sending the request. If any are wrong after creation, contact [email protected].


Step 2 — Upload the Required KYC Documents

Upload every KYC document type required by your onboarding with CoinMENA. The document upload endpoint accepts multipart/form-data. Front file is required; back file is optional (include it for two-sided documents such as national IDs).

The example below shows uploading a government_id. Repeat this call for each document type CoinMENA requires for the client — for example, you may also need to upload proof_of_address, residency_visa, source_of_funds, or others depending on your onboarding setup.

curl -X POST "https://external-api.coinmena.com/v1/partner/clients/user_12345/documents" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654322000" \
  -H "X-Signature: <your_generated_signature>" \
  -F "type=government_id" \
  -F "front_file=@/path/to/id_front.jpg" \
  -F "back_file=@/path/to/id_back.jpg"

Response — 201 Created

{
  "result": {
    "id": 1001,
    "type": "government_id",
    "front_file": "https://s3.me-south-1.amazonaws.com/private-bucket/clients/12345/government_id/abc.jpg?X-Amz-Expires=900&X-Amz-Signature=...",
    "back_file": "https://s3.me-south-1.amazonaws.com/private-bucket/clients/12345/government_id/def.jpg?X-Amz-Expires=900&X-Amz-Signature=...",
    "created_at": "2024-06-01T10:01:00Z"
  }
}
📘

Presigned URLs expire after 900 seconds

The front_file and back_file URLs in the response are presigned S3 URLs valid for 15 minutes. Do not store them — fetch the document metadata again when you need fresh URLs.

Supported Document Types

TypeDescription
government_idGovernment-issued ID (passport, national ID)
proof_of_addressUtility bill, bank statement, or similar
residency_visaResidency visa document
source_of_fundsDocumentation proving source of funds
bank_statementOfficial bank statement
inquiry_reportInquiry or background report

Accepted formats: JPEG, PNG, HEIC, HEIF, PDF. Maximum 5 MB per file.

Verify the Uploads

After uploading, you can list the documents attached to the client to confirm all required types are on file:

curl -X GET "https://external-api.coinmena.com/v1/partner/clients/user_12345/documents" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654323000" \
  -H "X-Signature: <your_generated_signature>"

The response lists every document currently on file for the client with its type, id, and presigned URLs.


Step 3 — Confirm the Client Is Ready

Fetch the client to confirm the record exists and the two flags you care about are set correctly. This is a sanity check — the GET call does not trigger any state change.

curl -X GET "https://external-api.coinmena.com/v1/partner/clients/user_12345/" \
  -H "X-Partner-ID: your_partner_id" \
  -H "X-Timestamp: 1737654324000" \
  -H "X-Signature: <your_generated_signature>"

Response — 200 OK

{
  "result": {
    "id": 12345,
    "partner_client_id": "user_12345",
    "status": "verified",
    "active": true,
    "name": "John Doe",
    "email": "[email protected]"
  }
}

Check both of the following on the returned record:

  • status: "verified" — the client is verified and allowed to transact
  • active: true — the client is not deactivated

When both conditions hold, onboarding is complete. The client is ready for funding (see the Funding & Reconciliation guide) and trading (see the Executing a Trade guide).


What Can Go Wrong

The most common errors during onboarding and what to do about them.

ErrorCodeLikely CauseFix
Client is under the legal age2021birth_date is below the jurisdiction's legal ageVerify the date of birth before submitting
Invalid residency / citizenship / place-of-birth country2022 / 2023 / 2024Country ID not supported, or not accepted for that fieldUse a country ID from GET /v1/public/countries. IDs are lowercase
Residency visa number and expiry are required2025Residency country differs from citizenship but visa details are missingSubmit both residency_visa_number and residency_visa_doe
Duplicate partner_client_id2026The reference ID is already used under your partner accountUse a new unique reference
Duplicate government ID / email / phone2016 / 2027 / 2028Another client already has this valueVerify the duplicate is intentional, otherwise use different values
Validation error422A required field is missing or has the wrong formatInspect msg_detail to find the failing field, then fix it
Invalid client ID on document upload2008partner_client_id in the URL does not match a client under your partner accountVerify you are using the same partner_client_id you set on creation
Unauthorized401Bad signature, expired timestamp, or IP not whitelistedWork through the Authentication troubleshooting checklist

For the complete list, see the Error Codes guide.


What’s Next
Next StepDescription
Funding & ReconciliationFund the client so they can trade
Executing a TradeRun the client's first trade end-to-end
ClientsFull reference for the client object, validation rules, and documents