Skip to main content
POST

Link Merchant

Link a business that already has a Khaime account to your marketplace as a sub-merchant. Use this when onboarding merchants who signed up independently before joining your marketplace.
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.

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 DECIMAL(5,4) in Postgres, and Sequelize returns DECIMAL columns as strings unless the code explicitly wraps them in Number().
  • Case 1 (new link): always a JS number (or null) — explicitly computed with Number(...).
  • Case 2 (already active): the raw DB value — a decimal string like "0.0500" (or null), not wrapped.
  • 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