KYC and KYB Flow

A sub-merchant cannot transact until they're verified and their wallet is provisioned. Inflow runs identity verification under the hood and exposes a unified contract (nextUrl + nextSteps) — you never integrate with a third-party verification provider directly.

Always treat the GET …/kyc/status response as the source of truth for what to do next — don't hard-code hosted URLs on your side.

When is a sub-merchant operable?

A sub-merchant is ready for X-On-Behalf-Of calls when all of the following are true:

  • kycReady is true on GET /api/connect/accounts/:subMerchantId/kyc/status
  • walletId is non-null (custodial wallet provisioned)
  • The sub-merchant is not suspended

Until then, payment and payout routes return 403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLE.

kycReady combines verification and wallet provisioning. If KYC passes but wallet creation fails, kycReady stays false and nextSteps may include retry_wallet_provisioning.

Response shapes by endpoint

Connect uses two related but distinct response shapes:

At creation — POST /api/connect/accounts

Returns the initial bootstrap state:

{
  "id": "usr_01JABCDEF…",
  "kycStatus": "pending",
  "verificationType": "KYC",
  "nextUrl": "https://verify.inflowpay.com/…",
  "nextSteps": ["redirect_submerchant_to_url", "…", "check_connect_kyc_status"],
  "kycBootstrapError": null
}

Use customerType ("individual" | "business") to choose KYC vs KYB.

At status poll — GET /api/connect/accounts/:subMerchantId/kyc/status

This is the live source of truth during onboarding:

{
  "subMerchantId": "usr_01JABCDEF…",
  "kycReady": false,
  "verificationType": "KYB",
  "nextUrl": "https://verify.inflowpay.com/…",
  "nextSteps": ["redirect_submerchant_to_url", "wait_for_aiprise_webhook_approval", "check_connect_kyc_status"],
  "pendingRequirements": ["proof_of_address", "company_registration"],
  "fallbackBridgeKycLink": "https://verify.inflowpay.com/…",
  "walletId": null
}
FieldMeaning
kycReadytrue when verification is complete and the wallet is provisioned
pendingRequirementsVerification items still outstanding
fallbackBridgeKycLinkAlternate hosted verification link (independent of ToS acceptance state)
walletIdCustodial wallet external id — non-null when operable

Poll until kycReady === true.

Some field and nextSteps values use legacy API identifiers. Treat them as opaque enums — branch on the exact strings returned, not on their names.

At read — GET /api/connect/accounts/:subMerchantId

Returns a cached snapshot for dashboard use:

{
  "id": "usr_01JABCDEF…",
  "connectKycStatus": "approved",
  "verificationType": "KYC",
  "custodialWalletAddress": "0xABC123…"
}
connectKycStatusMeaning
"pending"Verification in progress
"approved"Verification passed
"rejected"Verification declined — terminal for this account
nullNo cached status yet

For driving onboarding forward, always prefer GET …/kyc/status over the read endpoint.

The unified contract

nextUrl

The Inflow-hosted URL the sub-merchant must visit to make progress. null when there is nothing for the user to do (e.g. waiting for an async KYB decision).

nextSteps

An ordered string array describing what should happen next. Match on the exact strings returned by the API:

StepMeaning
redirect_submerchant_to_urlRedirect the user to nextUrl.
complete_bridge_tos_and_kyc_flowUser accepts Inflow ToS and completes identity verification on the hosted page.
wait_for_aiprise_webhook_approvalKYB form submitted — decision is async; keep polling.
accept_bridge_tosSub-merchant still has Inflow Terms of Service to accept; nextUrl points to the ToS page.
complete_remaining_bridge_onboardingUsed with accept_bridge_tos when both ToS acceptance and additional verification steps are pending.
check_connect_kyc_statusRe-poll the status endpoint after a delay.
retry_kyc_bootstrapInitial verification link failed to be issued; the next status poll will retry.
retry_wallet_provisioningVerification approved but wallet creation failed; re-poll to retry.

KYC happy path: ["redirect_submerchant_to_url", "complete_bridge_tos_and_kyc_flow", "check_connect_kyc_status"]

KYB happy path: ["redirect_submerchant_to_url", "wait_for_aiprise_webhook_approval", "check_connect_kyc_status"]

pendingRequirements

String identifiers for outstanding verification items (e.g. "proof_of_address", "company_registration", "terms_of_service"). Use these to show progress in your onboarding UI or to decide whether to redirect to nextUrl vs fallbackBridgeKycLink.

Polling for status

GET /api/connect/accounts/:subMerchantId/kyc/status
  ?kycEndorsement=&redirectUri=
X-Inflow-Api-Key: <inflow_apikey>
Query paramWhen to use
kycEndorsement ("sepa" | "spei" | "cards")Rail-aware hint when generating a hosted link. Only relevant if you need a link tuned for a specific payout rail.
redirectUriOverride the redirect URL used when (re-)generating a hosted link. Useful for routing back to a per-seller page.

Recommended cadence:

  • While the user is on the hosted page: don't poll — wait until they hit your redirectUrlKyc.
  • After redirect-back: poll every 2–3 seconds for up to ~30 seconds. KYC is usually instant; KYB can take longer.
  • If still not ready after 30s: stop polling, surface "we're verifying — we'll notify you when it's done", and rely on webhooks to wake your backend.

Retrying a failed bootstrap

If POST /api/connect/accounts returned kycStatus: "pending" with a kycBootstrapError, the sub-merchant exists but doesn't yet have a hosted link. Recovery is one extra call:

curl "https://api.inflowpay.xyz/api/connect/accounts/$SUB/kyc/status?redirectUri=https://marketplace.example.com/onboarding/$SELLER/done" \
  -H "X-Inflow-Api-Key: $INFLOW_API_KEY"

The status endpoint detects the missing session and re-issues a fresh nextUrl. No additional state to track on your side.

Handling rejection

connectKycStatus: "rejected" (or a terminal rejection from the status endpoint) is final for the current sub-merchant account. You cannot re-run verification on the same record — regulatory rules require a fresh customer.

  1. Surface a clear message to the seller (Inflow does not return a free-text rejection reason).
  2. Optionally suspend the sub-merchant via POST …/suspend so any pending obligations stop processing.
  3. If the seller wants to retry, sign them up with a different email — that creates a brand-new sub-merchant and KYC flow.

Security note

KYC state is server-controlled. Verification status, provider IDs, and approval flags are under Inflow's exclusive control — they cannot be set or overwritten via PATCH /api/connect/accounts/:id, even if you put them inside marketplaceMetadata.