curl -X GET https://api.khaime.com/api/v1/merchants/1676 \
-H "X-API-Key: pk_sandbox_your_key"
Marketplace
Get Merchant Details
Retrieve detailed information about a specific sub-merchant.
GET
/
merchants
/
{merchantId}
curl -X GET https://api.khaime.com/api/v1/merchants/1676 \
-H "X-API-Key: pk_sandbox_your_key"
Intro
Returns everything you need to know about one sub-merchant in your marketplace — profile, commission, payout readiness, KYC status, and product count — in a single call.Context
This is the detail view in the merchant lifecycle: use List Merchants to find amerchant_id, then call this endpoint for the full picture, or Update Merchant / Remove Merchant to change or suspend the relationship. The payout.charge_eligible field here is the same check Create Charge runs before accepting a marketplace charge for this merchant — check it here first rather than discovering it via a failed charge.
Hows
Path Parameters
string
required
The ID of the sub-merchant to retrieve.
Response
{
"success": true,
"message": "Merchant details retrieved successfully",
"data": {
"id": 1676,
"profile": {
"business_name": "Sub Store",
"business_email": "merchant@example.com",
"business_phone": "+2348012345678",
"business_country": "NG",
"subdomain": "substore",
"custom_domain": null,
"created_at": "2026-03-15T10:30:00.000Z"
},
"marketplace": {
"marketplace_id": 1042,
"status": "active",
"commission_rate": 0.08,
"commission_rate_inherited": false,
"joined_at": "2026-03-20T14:00:00.000Z"
},
"payout": {
"status": "active",
"settlement_currency": "NGN",
"payout_ready": true,
"charge_eligible": true,
"details": {
"type": "bank_transfer",
"bank_name": "Access Bank",
"bank_code": "044",
"account_number": "0123456789",
"account_name": "John Doe",
"date_connected": "2026-03-20T15:00:00.000Z"
}
},
"kyc": {
"verification_status": "kyc_approved",
"submission": {
"id": 42,
"status": "approved",
"business_type": "individual",
"settlement_currency": "NGN",
"country_mismatch": false,
"rejection_reason": null,
"submitted_at": "2026-03-20T15:00:00.000Z",
"reviewed_at": "2026-03-20T15:01:00.000Z"
}
},
"settings": {
"custom_platform_fee": null,
"custom_international_fee": null,
"customer_pays_transaction_fee": false
},
"stats": {
"product_count": 12
}
}
}
payout.details.account_number and, for international transfers, the Stripe account identifier are returned unmasked — this is the real bank account number / account ID, not a redacted placeholder. Handle this response as sensitive data: avoid logging it in plaintext or surfacing it directly in client-facing UI.Response Fields
profile
| Field | Type | Description |
|---|---|---|
business_name | string | Merchant’s business name |
business_email | string | Merchant’s email address |
business_phone | string | Merchant’s phone number |
business_country | string | Merchant’s country |
subdomain | string | Khaime subdomain |
custom_domain | string | Custom domain if configured |
created_at | string | Account creation timestamp |
marketplace
| Field | Type | Description |
|---|---|---|
marketplace_id | number | Your marketplace’s business ID |
status | string | Relationship status: active, suspended |
commission_rate | number | Commission rate (0-1, e.g., 0.08 = 8%) |
commission_rate_inherited | boolean | true if this merchant has no per-merchant override and is currently inheriting your marketplace’s default rate |
joined_at | string | When merchant joined the marketplace |
payout
| Field | Type | Description |
|---|---|---|
status | string | not_configured, pending_onboarding, or active |
settlement_currency | string | Currency the merchant’s KYC submission locked for payouts (e.g., NGN, USD), or null if no submission yet |
payout_ready | boolean | true once KYC is approved |
charge_eligible | boolean | Whether the merchant can receive charges right now. false means any charge with sub_merchant_id pointing to this merchant will be blocked |
details | object | null | Payout configuration details (see below), null if payout isn’t configured at all |
payout.details (Bank Transfer — NG, GH, ZA, KE)
{
"type": "bank_transfer",
"bank_name": "Access Bank",
"bank_code": "044",
"account_number": "0123456789",
"account_name": "John Doe",
"date_connected": "2026-03-20T15:00:00.000Z"
}
payout.details (International Transfer — US, GB, etc.)
{
"type": "international_transfer",
"account_id": "acct_1P...",
"verification_status": "kyc_approved",
"date_connected": "2026-03-20T15:00:00.000Z",
"bank_name": "Chase",
"last4": "6789",
"country": "US",
"currency": "USD"
}
bank_name, last4, country, currency, routing_number, account_holder_name) are fetched live from Stripe when a connected account exists, and are omitted if that lookup fails or no bank is on file yet.
kyc
| Field | Type | Description |
|---|---|---|
verification_status | string | Overall KYC stage (see values below) |
submission | object | null | Latest KYC submission, or null if none submitted |
submission.id | number | Submission ID |
submission.status | string | Submission-level status |
submission.business_type | string | individual or registered_business |
submission.settlement_currency | string | Currency locked for payouts |
submission.country_mismatch | boolean | true if identity and bank countries differ |
submission.rejection_reason | string | null | Set when status is rejected |
submission.submitted_at | string | ISO 8601 submission timestamp |
submission.reviewed_at | string | null | ISO 8601 review timestamp, or null if pending |
kyc.verification_status values
| Value | Description |
|---|---|
kyc_not_started | No KYC submitted yet |
kyc_pending_review | Awaiting review |
kyc_approved | Approved — payout account may be active |
kyc_rejected | Rejected — resubmission required |
kyc_additional_info_requested | Reviewer requested more documents |
kyc_revoked | Previously approved KYC revoked |
settings
| Field | Type | Description |
|---|---|---|
custom_platform_fee | number | null | Custom platform fee override |
custom_international_fee | number | null | Custom international fee override |
customer_pays_transaction_fee | boolean | Whether customer pays transaction fees |
stats
| Field | Type | Description |
|---|---|---|
product_count | number | Total products created by merchant |
Full setup flow
Before a sub-merchant can receive charges, complete the following steps:Step 1 — For NGN merchants only: fetch supported banks
GET /payout/banks?currency=NGN
Step 2 — Submit KYC (bank details are nested under bank_account for local bank countries)
POST /merchants/{merchantId}/kyc
→ { id_country, account_type, owner: {...}, business?: {...}, bank_account?: {...} }
Step 3 — Configure payout (if not auto-verified via KYC)
POST /merchants/{merchantId}/payout
NG/GH/ZA/KE → { country, settlement_bank, account_number, account_name }
US/GB/EU → { country, email } → redirect merchant to onboarding_url
Step 4 — Verify readiness
GET /merchants/{merchantId}
→ check kyc.verification_status === "kyc_approved"
→ check payout.charge_eligible === true
Step 5 — Charge
POST /payments/charge
→ include sub_merchant_id in the request
For local bank countries (NG, GH, ZA, KE), submitting KYC with full bank details can auto-verify payout in one step — no separate payout setup call needed if
kyc.verification_status comes back as kyc_approved and payout.charge_eligible is already true.| Country group | charge_eligible: true when |
|---|---|
NGN (NG) | KYC approved with bank details (auto) or after separate payout setup |
StartButton (GH, ZA, KE) | KYC approved with bank details (auto) or after separate payout setup |
International (US, GB, CA, EU) | After merchant completes Connect onboarding via onboarding_url |
Error Codes
| Status | Error Code | Cause |
|---|---|---|
403 | AUTH_PERMISSION_DENIED | Your business isn’t set up as a marketplace operator. |
404 | BUSINESS_NOT_FOUND | The merchant is not linked to your marketplace, or the merchant account doesn’t exist. |
curl -X GET https://api.khaime.com/api/v1/merchants/1676 \
-H "X-API-Key: pk_sandbox_your_key"
Whys
charge_eligible is deliberately separate from status and payout_ready because “linked to the marketplace” and “KYC approved” are each necessary but not sufficient for “can actually receive money right now.” A merchant can have an active relationship and approved KYC and still be missing the real-time payout account (a local transfer account for NGN, Stripe connected account for international) that a charge actually settles into. Exposing one boolean that mirrors the exact check Create Charge runs means you can validate readiness before attempting a charge instead of parsing a payment failure to figure out why it was blocked.
KYC status and payout status are read from the same underlying KYC submission record rather than two independently-updated fields, so they can’t drift out of sync with each other.
Why nots
This endpoint does not let you modify anything — it’s read-only. Use Update Merchant for profile/commission changes, the KYC endpoints for identity verification, and the payout endpoints for bank/Stripe configuration. It does not mask sensitive payout details (account numbers, Stripe account IDs) — treat the response accordingly in your own logging and UI.settlement_currency reflects the currency locked by the merchant’s KYC submission, not a currency you can set directly through this or the merchant-update endpoint.