Skip to main content
POST

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.

Response

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.
An entry with an existing active relationship is not re-linked at all — it goes to 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 in results.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

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 returning 201 (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 check data.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.