Skip to main content
POST

Intro

Attaches a business that already has a Khaime account to your marketplace as a sub-merchant — no new account is created.

Context

Part of the merchant lifecycle alongside Create Merchant (which is for businesses with no Khaime account yet) and Import Merchants (bulk version of both). This is also the endpoint that reactivates a merchant previously detached via Remove Merchant — call it again with the same email rather than looking for a separate “reactivate” call.
If the business doesn’t have a Khaime account yet, use Create Merchant (single) or Import Merchants (bulk) instead — both create the account for you. This endpoint only links existing accounts.

Hows

Request Body

string
required
Email of the existing Khaime business to link. Must match an account exactly — there’s no fuzzy matching.
number
Decimal commission rate between 0 and 1 for this merchant. If omitted, the merchant inherits your marketplace’s default rate dynamically — meaning if you change your portfolio default later, this merchant’s effective rate changes too. Pass an explicit value only if you want this merchant pinned to a fixed rate regardless of future default changes.

What happens depends on prior state

The response data shape differs depending on which of these three cases applies — don’t assume a fixed schema.
1. Not linked before → creates a new relationship, status: active. Response is 201.
2. Already linked and active → no-op, idempotent. Response is 200 and omits commission_rate_inherited and marketplace_id present in case 1:
3. Previously linked, then suspended → reactivates the existing relationship (status: active, joined_at reset to now). Response is 200, shape matches case 1 (includes commission_rate_inherited), message "Merchant reactivated in your marketplace". In all three cases, if commission_rate is explicitly provided, it overwrites the stored rate; if omitted on reactivation, the previously stored rate is kept.
commission_rate’s type is inconsistent across these three cases — don’t assume it’s always a number. The column is a Postgres decimal, and it’s returned as a string unless the code explicitly converts it.
  • Case 1 (new link): always a JS number (or null) — explicitly converted.
  • Case 2 (already active): the raw database value — a decimal string like "0.0500" (or null), not converted.
  • Case 3 (reactivated): a string if a previously stored rate exists and you didn’t pass an explicit commission_rate; a number only if it falls back to the computed default.
If you’re storing this value, parse it with Number(...) on your side regardless of which case you hit — don’t rely on typeof commission_rate === 'number'.

Error Codes

Whys

Linking is split from creation because a meaningful share of merchants joining a marketplace already have a Khaime account from before they had any marketplace relationship — forcing account re-creation would either fail on the duplicate email or create confusing duplicate accounts. Making this endpoint double as the reactivation path (rather than requiring a separate “reactivate” call) means operators only need to remember one endpoint for “make sure this email is an active sub-merchant,” regardless of whether it’s new, existing-and-unlinked, or previously suspended. The dynamic inheritance behavior for commission_rate (when omitted) lets operators change their marketplace-wide default rate once and have it apply to every merchant that hasn’t been pinned to a custom rate, rather than needing to update every merchant individually.

Why nots

This endpoint does not create a new Khaime account — if the email doesn’t match an existing one, it fails with BUSINESS_NOT_FOUND rather than falling back to account creation. It also doesn’t let you link your own operator account as a sub-merchant of itself. For bulk onboarding where some emails are new and some already exist, use Import Merchants instead of calling this endpoint in a loop — it handles the create-vs-link decision per entry and reports results per row.