curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/link \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"business_email": "ade@leatherworks.com",
"commission_rate": 0.05
}'
Marketplace
Link Merchant
Link an existing Khaime business as a sub-merchant in your marketplace.
POST
/
marketplace
/
merchants
/
link
curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/link \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"business_email": "ade@leatherworks.com",
"commission_rate": 0.05
}'
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.status: active. Response is 201.
{
"success": true,
"message": "Merchant linked to marketplace successfully",
"data": {
"id": 14,
"merchant_id": 892,
"business_name": "Ade Leatherworks",
"business_email": "ade@leatherworks.com",
"commission_rate": 0.05,
"commission_rate_inherited": true,
"marketplace_id": 42,
"status": "active"
}
}
200 and omits commission_rate_inherited and marketplace_id present in case 1:
{
"success": true,
"message": "Merchant is already linked and active in your marketplace",
"data": {
"id": 14,
"merchant_id": 892,
"business_name": "Ade Leatherworks",
"business_email": "ade@leatherworks.com",
"commission_rate": "0.0500",
"status": "active"
}
}
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"(ornull), 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.
Number(...) on your side regardless of which case you hit — don’t rely on typeof commission_rate === 'number'.Error Codes
| Status | Error Code | Cause |
|---|---|---|
403 | AUTH_PERMISSION_DENIED | Your business isn’t a marketplace operator. Run Setup Marketplace first. |
400 | VALIDATION_MISSING_FIELD | business_email is missing. |
400 | VALIDATION_FAILED | commission_rate given but outside 0–1, or you tried to link your own business as its own sub-merchant. |
404 | BUSINESS_NOT_FOUND | No Khaime account exists with that email — use Create Merchant instead. |
curl -X POST https://api.khaime.com/api/v1/marketplace/merchants/link \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"business_email": "ade@leatherworks.com",
"commission_rate": 0.05
}'
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 forcommission_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 withBUSINESS_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.