Sub-merchant Onboarding
A sub-merchant is a separate Inflow account owned by your marketplace. It has its own KYC, its own balance, and its own payout destination. The marketplace creates them, updates their profile, suspends/resumes them, and acts on their behalf.
All routes below are authenticated with your marketplace API key. Sub-merchants don't authenticate against the Inflow API directly — every action is mediated by the parent marketplace.
Creating a sub-merchant
POST /api/connect/accounts
Authorization: Bearer <inflow_apikey>
Content-Type: application/jsonRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | yes | Sub-merchant's contact email. Must not already be used by any Inflow user. |
bridgeCustomerType | "individual" | "business" | no (default "individual") | Drives the verification flow: individual → KYC, business → KYB. |
merchantName | string | no | Display name. Defaults to "SubMerchant" if omitted. |
firstName | string | no (used for individuals) | If omitted, derived from merchantName / email. |
lastName | string | no (used for individuals) | Defaults to "Merchant" if omitted. |
marketplaceMetadata | object | no | Arbitrary metadata you want to associate (your seller id, tier, etc.). Inflow-reserved keys are silently dropped. |
redirectUrlKyc | string (URL) | no | Where to redirect the sub-merchant after they complete KYC. Falls back to defaultRedirectUrlKyc from your marketplace settings. |
Response (201 Created):
{
"subMerchantId": "usr_01JABCDEF…",
"kycStatus": "pending",
"verificationType": "KYC",
"verificationProvider": "BRIDGE",
"nextUrl": "https://bridge.with…/onboarding?session=…",
"nextSteps": [
"redirect_submerchant_to_url",
"complete_bridge_tos_and_kyc_flow",
"check_connect_kyc_status"
],
"email": "[email protected]",
"merchantName": "Acme Coffee",
"marketplaceMetadata": { "yourSellerId": "S-12" }
}The response is the unified KYC contract — nextUrl and nextSteps are the single source of truth for what the marketplace should do next. See KYC Flow for the full state machine.
Individual vs. business
bridgeCustomerType | Verification flow | What happens after approval |
|---|---|---|
"individual" | KYC (hosted link) | Inflow provisions the sub-merchant's wallet automatically. |
"business" | KYB (hosted link) | Inflow finalises the business identity and provisions the sub-merchant's wallet automatically. |
In both cases the sub-merchant is not operable until kycStatus = "approved". X-On-Behalf-Of calls return 403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLE until then.
Failure-tolerant semantics
If the initial KYC bootstrap fails after the sub-merchant has been created (e.g. provider timeout), the response still includes a subMerchantId so you don't lose track of it:
{
"subMerchantId": "usr_…",
"kycStatus": "pending",
"kycBootstrapError": "hosted-kyc-link timeout",
"nextSteps": ["retry_kyc_bootstrap", "check_connect_kyc_status"]
}The sub-merchant exists and you can retry by calling GET /api/connect/accounts/:subMerchantId/kyc/status, which re-issues the verification link.
Listing sub-merchants
GET /api/connect/accounts?page=1&limit=20&email=&merchantName=
Authorization: Bearer <inflow_apikey>| Query param | Type | Default | Description |
|---|---|---|---|
page | int (≥ 1) | 1 | Page number, 1-indexed. |
limit | int (1–100) | 20 | Items per page. |
email | string | — | Exact-match (case-insensitive) email filter. Useful to resolve a sub-merchant by the email you signed it up with. |
merchantName | string | — | Case-insensitive substring match on merchantName. |
Response shape matches the Inflow paginated convention:
{
"data": [ { "id": "usr_…", "email": "…", "merchantName": "…", "kycStatus": "approved", … } ],
"meta": { "total": 42, "lastPage": 3, "currentPage": 1, "perPage": 20, "prev": null, "next": 2 }
}Listings are always scoped to the calling marketplace — cross-marketplace reads are structurally impossible.
Reading a sub-merchant
GET /api/connect/accounts/:subMerchantId
Authorization: Bearer <inflow_apikey>Returns the full sub-merchant profile, including:
- Identity:
email,merchantName,firstName,lastName,marketplaceMetadata. - Wallet info: provider, address.
- Operability:
kycStatus,suspended, plus a freshnextUrl/nextStepsif KYC is still in progress.
404 if the sub-merchant doesn't exist or doesn't belong to your marketplace.
Updating a sub-merchant's profile
PATCH /api/connect/accounts/:subMerchantId
Authorization: Bearer <inflow_apikey>
Content-Type: application/jsonOnly these fields are accepted:
| Field | Notes |
|---|---|
merchantName | Display name. |
firstName | Individual sub-merchants only. |
lastName | Individual sub-merchants only. |
marketplaceMetadata | Shallow-merged into the existing object. Inflow-reserved keys are silently dropped. |
Anything else — email, identity / KYC provider IDs, wallet pointers, fee rates, suspension flag — is intentionally not editable here. KYC fields are tied to the verification record, fees live in your marketplace settings, and suspension uses the dedicated routes documented in Suspension and Lifecycle.
Suspend / resume
See Suspension and Lifecycle for POST /api/connect/accounts/:subMerchantId/suspend and …/resume.
End-to-end snippet
const sub = await fetch("https://api.inflowpay.xyz/api/connect/accounts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.INFLOW_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email: seller.email,
merchantName: seller.shopName,
bridgeCustomerType: "individual",
firstName: seller.firstName,
lastName: seller.lastName,
marketplaceMetadata: { yourSellerId: seller.id },
redirectUrlKyc: `https://marketplace.example.com/onboarding/${seller.id}/done`,
}),
}).then(r => r.json());
// Persist `sub.subMerchantId` against your seller, then redirect:
res.redirect(sub.nextUrl);When the seller comes back to your redirectUrlKyc, poll GET /api/connect/accounts/:subMerchantId/kyc/status until kycStatus === "approved" — see KYC Flow.
Updated about 19 hours ago