curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/import \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"merchants": [
{ "business_email": "new@biz.com", "business_name": "New Biz", "commission_rate": 0.1 },
{ "business_email": "existing@biz.com" }
]
}'
Marketplace
Import Merchants
Bulk-create and/or link up to 100 sub-merchants in a single request.
POST
/
marketplace
/
merchants
/
import
curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/import \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"merchants": [
{ "business_email": "new@biz.com", "business_name": "New Biz", "commission_rate": 0.1 },
{ "business_email": "existing@biz.com" }
]
}'
Intro
Bulk-onboards sub-merchants in one call: for each entry it creates a new Khaime account if the email doesn’t exist yet, or links the existing account if it does.Context
This combines what Create Merchant and Link Merchant do individually, applied per row of a batch — use it instead of looping over those two endpoints when onboarding a merchant list from an external system (a CSV export, another marketplace platform, etc.). The resulting merchants show up exactly like any other in List Merchants and Get Merchant Details.Hows
This endpoint always returns
201, even if every single entry fails. There is no top-level error status for partial or total per-entry failure — you must inspect data.summary.failed / data.results.failed to know what actually happened. Don’t treat a 201 as “all merchants imported successfully.”Request Body
array
required
Non-empty array, maximum 100 entries per request.
Show Merchant entry fields
Show Merchant entry fields
string
required
Required for every entry. If missing or not a string, that entry is recorded in
results.failed — it does not fail the whole request.string
Required only when no existing Khaime account matches
business_email (i.e. this entry will create a new account). Omit it when linking an existing account.string
Optional, used only on account creation.
string
Optional, used only on account creation.
number
Optional decimal between
0 and 1 for this entry. Same inheritance behavior as Link Merchant when omitted.Response
{
"success": true,
"message": "Import complete: 1 created, 1 linked, 0 skipped, 1 failed",
"data": {
"summary": { "total": 3, "created": 1, "linked": 1, "skipped": 0, "failed": 1, "succeeded": 2 },
"results": {
"created": [
{
"email": "new@biz.com",
"merchant_id": 900,
"business_name": "New Biz",
"subdomain": "new-biz",
"temporary_password": "a1b2c3d4e5f6a1b2c3d4e5f6"
}
],
"linked": [
{ "email": "existing@biz.com", "merchant_id": 55, "business_name": "Existing Biz", "action": "linked" }
],
"skipped": [
{ "email": "already-active@biz.com", "merchant_id": 33, "business_name": "Already Active Biz", "reason": "Already linked and active" }
],
"failed": [
{ "email": "bad@biz.com", "reason": "commission_rate must be a decimal between 0 and 1" }
]
}
}
}
temporary_password is returned in plaintext for every newly created account. This is the account’s real login password — treat the response as sensitive, don’t log it in plaintext analytics or client-visible logs, and prompt the merchant to change it on first login.linked entries: action is "linked" or "reactivated"
An entry lands in results.linked in two different situations, distinguished by action:
"linked"— an existing Khaime account with no prior relationship to your marketplace was linked for the first time."reactivated"— the account had a previously suspended relationship with your marketplace, now reactivated.
results.skipped instead (see below), not results.linked.
skipped entries
An entry lands here only for one reason today: reason: "Already linked and active" — the email already has an active relationship in your marketplace, so nothing changed.
Per-entry failure reasons
Each item inresults.failed has a reason string, one of:
"business_email is required"— missing or non-string email"commission_rate must be a decimal between 0 and 1""Cannot link your own account as a sub-merchant"— entry’s email matches your own operator account"business_name is required when creating a new account (no existing Khaime account found)"- any other caught exception message (generic, not enumerable)
Error Codes
| Status | Error Code | Cause |
|---|---|---|
403 | AUTH_PERMISSION_DENIED | Your business isn’t a marketplace operator. |
400 | VALIDATION_FAILED | merchants missing, not an array, empty, or exceeds 100 entries. |
400 | INTERNAL_ERROR | Unhandled top-level error (individual entry failures do not trigger this — see per-entry failed array above). |
curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/import \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"merchants": [
{ "business_email": "new@biz.com", "business_name": "New Biz", "commission_rate": 0.1 },
{ "business_email": "existing@biz.com" }
]
}'
Whys
Import processes each entry independently rather than as an all-or-nothing transaction, because a single malformed row in a 100-entry batch (a typo’d email, a missing name) shouldn’t force you to fix and resubmit the entire batch — the 99 good entries succeed and the bad one is reported individually. Always returning201 (rather than a mixed status code) reflects that the request itself was accepted and processed; the per-entry results are where success or failure actually lives, similar to how a bulk import job would report a run summary rather than a single pass/fail status.
The 100-entry cap keeps each request’s processing time and transactional footprint bounded, since each entry does its own account-creation and database-linking work sequentially.
Why nots
This is not a transactional all-or-nothing operation — partial success is the expected outcome, not an edge case. Don’t build error handling that assumes a non-2xx status means nothing happened; always checkdata.summary. It also does not update existing merchants’ profile fields — an entry for an already-linked email is skipped, not used to update that merchant’s details (use Update Merchant for that). For a single merchant, Create Merchant or Link Merchant are simpler and give a single, non-nested response.