curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "individual",
"owner": {
"first_name": "Amara",
"last_name": "Osei",
"date_of_birth": "1990-05-15",
"phone_number": "+2348012345678",
"email": "amara@example.com",
"id_type": "national_id",
"id_number": "12345678901",
"id_document_url": "https://cdn.example.com/docs/amara-id.jpg",
"selfie_url": "https://cdn.example.com/docs/amara-selfie.jpg",
"address": {
"street": "12 Victoria Island",
"city": "Lagos",
"state": "Lagos",
"postal_code": "101001",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "Access Bank",
"account_number": "0123456789",
"account_name": "Amara Osei"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "registered_business",
"owner": {
"first_name": "Chidi",
"last_name": "Okoro",
"id_type": "passport",
"id_number": "A12345678",
"id_document_url": "https://cdn.example.com/docs/chidi-passport.jpg",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"business": {
"legal_name": "Okoro Enterprises Ltd",
"trading_name": "Okoro Store",
"registration_number": "RC123456",
"tax_id": "TIN12345678",
"business_type": "llc",
"registration_document_url": "https://cdn.example.com/docs/cac-cert.pdf",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "GTBank",
"account_number": "0234567890",
"account_name": "Okoro Enterprises Ltd"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/456/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "US",
"business_type": "individual",
"return_url": "https://yourplatform.com/merchants/456/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/456/kyc"
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/789/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "GB",
"business_type": "registered_business",
"return_url": "https://yourplatform.com/merchants/789/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/789/kyc"
}'
Marketplace
Submit Merchant KYC
Submit a KYC application on behalf of a sub-merchant.
POST
/
merchants
/
{merchantId}
/
kyc
curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "individual",
"owner": {
"first_name": "Amara",
"last_name": "Osei",
"date_of_birth": "1990-05-15",
"phone_number": "+2348012345678",
"email": "amara@example.com",
"id_type": "national_id",
"id_number": "12345678901",
"id_document_url": "https://cdn.example.com/docs/amara-id.jpg",
"selfie_url": "https://cdn.example.com/docs/amara-selfie.jpg",
"address": {
"street": "12 Victoria Island",
"city": "Lagos",
"state": "Lagos",
"postal_code": "101001",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "Access Bank",
"account_number": "0123456789",
"account_name": "Amara Osei"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "registered_business",
"owner": {
"first_name": "Chidi",
"last_name": "Okoro",
"id_type": "passport",
"id_number": "A12345678",
"id_document_url": "https://cdn.example.com/docs/chidi-passport.jpg",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"business": {
"legal_name": "Okoro Enterprises Ltd",
"trading_name": "Okoro Store",
"registration_number": "RC123456",
"tax_id": "TIN12345678",
"business_type": "llc",
"registration_document_url": "https://cdn.example.com/docs/cac-cert.pdf",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "GTBank",
"account_number": "0234567890",
"account_name": "Okoro Enterprises Ltd"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/456/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "US",
"business_type": "individual",
"return_url": "https://yourplatform.com/merchants/456/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/456/kyc"
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/789/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "GB",
"business_type": "registered_business",
"return_url": "https://yourplatform.com/merchants/789/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/789/kyc"
}'
Intro
Submits a Know Your Customer (KYC) application for a sub-merchant in your marketplace — the identity, business, and bank verification step that must be approved before that merchant can receive payouts or be charged against.Context
KYC sits between merchant creation and payout. The flow is: create or link the merchant, submit KYC, then check outcome with Get KYC Status — if rejected, correct and retry with Resubmit KYC. Khaime routes verification to one of two providers based on the merchant’s country, and this single endpoint handles both:| Country | Provider | Verification | Data collection |
|---|---|---|---|
NG, GH, ZA, KE | Khaime KYC | Reviewed by Khaime within 24 hours | You submit documents via this API |
Everywhere else (US, GB, CA, EU, etc.) | Stripe Connect | Automated via Stripe | Merchant completes Stripe’s hosted onboarding |
The merchant must already exist in your marketplace before you submit KYC for them. Create or link them first via Create Merchant.
Hows
Path parameters
number
required
The ID of the sub-merchant.
Request body — African countries (Khaime KYC)
string
required
ISO 3166-1 alpha-2 country code. Must be one of
NG, GH, ZA, KE.string
required
individual or registered_business.Owner object (required)
object
required
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Owner’s first name |
last_name | string | Yes | Owner’s last name |
date_of_birth | string | No | YYYY-MM-DD |
phone_number | string | No | Contact phone number |
email | string | No | Contact email address |
id_type | string | Yes | passport, national_id, or drivers_license |
id_number | string | Yes | ID number (BVN, NIN, or passport number) |
id_document_url | string | Yes | Publicly accessible URL to the identity document image |
selfie_url | string | No | URL to a selfie photo for liveness verification |
address | object | Yes | street, city, state (all required), postal_code (optional), country (required, 2-letter code) |
Business object (required when account_type is registered_business)
object
| Field | Type | Required | Description |
|---|---|---|---|
legal_name | string | Yes | Registered business name |
trading_name | string | No | Trading/DBA name if different |
registration_number | string | Yes | Business registration number (e.g., CAC number) |
tax_id | string | No | Tax identification number |
business_type | string | No | sole_proprietorship, partnership, llc, corporation, ngo, other |
website | string | No | Business website URL |
phone_number | string | No | Business phone number |
registration_document_url | string | Yes | URL to certificate of incorporation |
additional_document_url | string | No | URL to a supporting document |
address | object | Yes | Same shape as owner.address |
Bank account object (optional at submission time)
object
| Field | Type | Required | Description |
|---|---|---|---|
country | string | Yes | Bank country code (e.g., NG, GH) |
bank_name | string | Yes* | Bank name from Get Supported Banks |
bank_id | string (UUID) | Yes* | Bank ID from Get Supported Banks |
account_number | string | Yes | Bank account number |
account_name | string | Yes | Name on the bank account |
sort_code | string | No | Required for UK accounts |
routing_number | string | No | For US accounts |
bank_code | string | No | Bank code, if applicable |
bank_name or bank_id must be present. For the most reliable result, pass bank_name — it’s what the rest of the payout pipeline reads back.If you skip
bank_account here, add it afterward with Setup Merchant Payout. For NG merchants specifically, using Setup Payout also resolves the bank to the correct local transfer rail — worth doing even if you already sent bank details with KYC.Request body — everywhere else (Stripe Connect)
For non-African countries, Stripe collects identity, business, and bank information directly through its hosted flow — you only supply enough to start the session.string
required
ISO 3166-1 alpha-2 country code (e.g.
US, GB, CA, DE, FR, AU).string
individual or registered_business. Defaults to individual.string
Settlement currency for payouts. Derived from
country if omitted.string
required
Where the merchant lands after completing Stripe onboarding. Must be a page on your platform, not Khaime’s.
string
required
Where the merchant lands if the onboarding link expires or they exit early. Typically the same page that started KYC.
Response — African countries
{
"success": true,
"message": "KYC submitted for review. We will notify you within 24 hours.",
"data": {
"provider": "khaime",
"id": 42,
"status": "pending_review",
"business_type": "individual",
"legal_name": "Amara Osei",
"id_country": "NG",
"bank_name": "Access Bank",
"bank_account_number": "0123456789",
"bank_account_name": "Amara Osei",
"settlement_currency": "NGN",
"country_mismatch": false,
"submitted_at": "2026-04-10T12:00:00.000Z",
"reviewed_at": null,
"rejection_reason": null
}
}
status: "approved" immediately rather than pending_review — treat both as a valid outcome and always read status from the response instead of assuming a fixed 24-hour wait.
Response — everywhere else (Stripe Connect)
{
"success": true,
"message": "Connect onboarding initiated. Complete verification on the hosted flow.",
"data": {
"provider": "stripe",
"submission_id": 43,
"stripe_account_id": "acct_1234567890",
"onboarding_url": "https://connect.khaime.com/setup/s/abc123",
"client_secret": "accs_1234567890_secret_xyz",
"publishable_key": "pk_test_..."
}
}
| Field | Description |
|---|---|
onboarding_url | Redirect the merchant here for hosted, out-of-app onboarding |
client_secret + publishable_key | Use together with Stripe Connect Embedded Components to keep the merchant on your site |
stripe_account_id | The Stripe Connect account ID created for this merchant |
Calling this endpoint again for a merchant who already has an active Stripe session reuses their existing connected account and simply returns a fresh session — it will not create a duplicate Stripe account.
Error cases
| Status | Error | Fix |
|---|---|---|
400 | country or id_country is required | Include the country field |
400 | account_type is required / owner object is required for African markets / business object is required for registered businesses | Joi validation error — response is { "message": "Validation error", "details": [...] }; check details for the exact missing field |
400 | return_url and refresh_url are required for Stripe Connect onboarding | Include both redirect URLs for non-African countries |
401 | Missing or invalid X-API-Key header | Include a valid Partner API key |
403 | This endpoint is restricted to marketplace operators | Your API key must belong to a marketplace operator account |
404 | Active merchant relationship not found | The merchant is not linked to your marketplace |
owner.id_document_url) are caught before your handler code runs and return a plain { message, details } body with no error_code. Business-rule failures caught inside the handler (missing country, missing Stripe redirect URLs, permission checks) return the standard envelope with error_code: VALIDATION_FAILED, AUTH_PERMISSION_DENIED, or BUSINESS_NOT_FOUND.
curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "individual",
"owner": {
"first_name": "Amara",
"last_name": "Osei",
"date_of_birth": "1990-05-15",
"phone_number": "+2348012345678",
"email": "amara@example.com",
"id_type": "national_id",
"id_number": "12345678901",
"id_document_url": "https://cdn.example.com/docs/amara-id.jpg",
"selfie_url": "https://cdn.example.com/docs/amara-selfie.jpg",
"address": {
"street": "12 Victoria Island",
"city": "Lagos",
"state": "Lagos",
"postal_code": "101001",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "Access Bank",
"account_number": "0123456789",
"account_name": "Amara Osei"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/123/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"id_country": "NG",
"account_type": "registered_business",
"owner": {
"first_name": "Chidi",
"last_name": "Okoro",
"id_type": "passport",
"id_number": "A12345678",
"id_document_url": "https://cdn.example.com/docs/chidi-passport.jpg",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"business": {
"legal_name": "Okoro Enterprises Ltd",
"trading_name": "Okoro Store",
"registration_number": "RC123456",
"tax_id": "TIN12345678",
"business_type": "llc",
"registration_document_url": "https://cdn.example.com/docs/cac-cert.pdf",
"address": {
"street": "45 Broad Street",
"city": "Lagos",
"state": "Lagos",
"country": "NG"
}
},
"bank_account": {
"country": "NG",
"bank_name": "GTBank",
"account_number": "0234567890",
"account_name": "Okoro Enterprises Ltd"
}
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/456/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "US",
"business_type": "individual",
"return_url": "https://yourplatform.com/merchants/456/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/456/kyc"
}'
curl -X POST "https://api.khaime.com/api/v1/merchants/789/kyc" \
-H "X-API-Key: pk_sandbox_your_key" \
-H "Content-Type: application/json" \
-d '{
"country": "GB",
"business_type": "registered_business",
"return_url": "https://yourplatform.com/merchants/789/kyc-complete",
"refresh_url": "https://yourplatform.com/merchants/789/kyc"
}'
Whys
One endpoint, two providers. Rather than forcing you to branch on country before calling the API, the endpoint inspectsid_country/country and routes internally. This keeps your integration code simple — the trade-off is that the request shape genuinely differs by provider, so read the country column before building your payload.
Structured owner/business/bank_account objects. Khaime KYC used to accept a flat list of top-level fields (legal_name, id_document_type, business_address, and similar). The API has moved to nested objects because the shape maps directly onto the underlying identity, business, and bank records reviewers work from, and it scales cleanly to fields like business.tax_id that don’t have an obvious flat-field name. Only the structured format is accepted now — see Why Nots.
Stripe for everything outside Africa. Building and maintaining compliant identity verification per country is Stripe’s core product; Khaime’s own reviewers focus on the African markets where Stripe Connect’s coverage and payout rails are thinner. This is also why non-African submissions barely validate anything server-side — Stripe owns that surface.
Why nots
- The legacy flat request format no longer works. Fields like
legal_name,id_document_type,business_address,incorporation_cert_url,bank_country, or top-levelbank_name/bank_account_numberare silently stripped by request validation before they reach any handling code. A request built only with those fields will fail withaccount_type is required/owner object is required for African markets, because none of your data survived. Always use the structuredowner/business/bank_accountobjects. - This is not a document upload endpoint.
id_document_url,selfie_url, and the business document URLs must already be publicly reachable (e.g., hosted on your own storage or CDN) before you call this endpoint. Khaime does not host uploads for you here. - Not a status endpoint. The response tells you the outcome of this submission, not the merchant’s current overall verification state — poll Get KYC Status for that.
- Approved KYC is locked. Calling this endpoint again for a merchant whose KYC is already
approvedwon’t change anything for Khaime KYC merchants and isn’t the right tool for updating bank details post-approval — use Update Merchant Payout instead. - No automatic retries against Stripe. If Stripe onboarding creation fails transiently, you need to call this endpoint again yourself; nothing retries it for you.
Next Steps
- Check the submission outcome with Get KYC Status
- If rejected, use Resubmit KYC with corrected documents
- Configure or confirm bank details with Setup Merchant Payout
