Acting on Behalf of a Sub-merchant

A marketplace's API key has two personalities:

  1. The marketplace itself — used to call the Connect-management routes (/api/connect/*).
  2. A specific sub-merchant — used for everything that touches money: payments, payouts, customers, payment links, subscriptions, and so on.

Switching between them is a single HTTP header.

The X-On-Behalf-Of header

X-On-Behalf-Of: usr_01JABCDEF…

Set the value to the subMerchantId you want to act as. The header is case-insensitive; both X-On-Behalf-Of and x-on-behalf-of work.

When a marketplace API key calls a merchant route:

  • Without the header → request is rejected with 400 ON_BEHALF_REQUIRED_FOR_MARKETPLACE. Marketplaces have no first-class merchant balance, so there's no sensible default sub-merchant.
  • With a valid header → the request is treated as if the sub-merchant itself made it. The customer record, the payment's owner, the wallet to credit, and every other side-effect resolve to the sub-merchant.

Non-marketplace API keys (regular merchants, individuals) must not send the header. If they do, the request is rejected with 403 ON_BEHALF_FORBIDDEN_CALLER_TYPE.

Which routes accept the header?

Every standard Inflow merchant API endpoint — payments, hosted checkout, payment links, customers, payment methods, subscriptions, withdrawals, account management, and so on. You don't need to learn a new API surface to operate as a sub-merchant; you just call the same routes you would call as a merchant and add the header.

The only exceptions are the Connect-management routes themselves (/api/connect/*), which always operate on the calling marketplace and do not accept the header.

If you're unsure whether a specific route accepts the header, calling it without the header from a marketplace key is a clean check: you'll either get 400 ON_BEHALF_REQUIRED_FOR_MARKETPLACE (it does, you just need to set it) or normal route behavior (it doesn't).

Validity rules

For a marketplace + X-On-Behalf-Of: <id> request to succeed, all of these must hold:

  1. The calling marketplace's Connect access is active (not paused, not disabled).
  2. The target sub-merchant exists and belongs to the calling marketplace — you can't borrow another marketplace's seller.
  3. The target sub-merchant is operable: KYC is approved and they're not currently suspended.

If any rule fails, the request is rejected before reaching the route handler.

Error codes

All errors come back with a JSON body shaped:

{
  "statusCode": 403,
  "message": "…",
  "errorCode": "…"
}
HTTPerrorCodeWhen
400ON_BEHALF_REQUIRED_FOR_MARKETPLACEMarketplace caller hit a merchant route without the header.
403ON_BEHALF_FORBIDDEN_CALLER_TYPEA non-marketplace caller (regular merchant) tried to use the header.
404ON_BEHALF_SUBMERCHANT_NOT_FOUNDThe id in the header doesn't correspond to a sub-merchant.
403ON_BEHALF_SUBMERCHANT_NOT_OWNEDThe sub-merchant exists but belongs to another marketplace.
403ON_BEHALF_SUBMERCHANT_NOT_OPERABLEKYC not approved, or the sub-merchant is currently suspended.
403ON_BEHALF_MARKETPLACE_PAUSEDThe calling marketplace's Connect access is currently paused.
403ON_BEHALF_CONNECT_DISABLEDConnect is disabled on the calling marketplace.

The same compliance kill-switches surface on the Connect-management routes too, with shorter codes: 403 MARKETPLACE_PAUSED and 403 CONNECT_DISABLED. See Suspension and Lifecycle.

Examples

Create a payment for usr_seller_42

curl -X POST https://api.inflowpay.xyz/api/payment/checkout/payment \
  -H "Authorization: Bearer $INFLOW_API_KEY" \
  -H "X-On-Behalf-Of: usr_seller_42" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1500,
    "currency": "EUR",
    "successUrl": "https://shop.example.com/success",
    "cancelUrl":  "https://shop.example.com/cancel"
  }'

List a sub-merchant's customers

curl "https://api.inflowpay.xyz/api/customer?page=1&limit=20" \
  -H "Authorization: Bearer $INFLOW_API_KEY" \
  -H "X-On-Behalf-Of: usr_seller_42"

Trigger a payout for a sub-merchant

curl -X POST https://api.inflowpay.xyz/api/withdraw/... \
  -H "Authorization: Bearer $INFLOW_API_KEY" \
  -H "X-On-Behalf-Of: usr_seller_42" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 50000, "currency": "USDC", "destination": "…" }'

Webhooks

Inflow's standard webhooks (payment.*, subscription.*, withdraw.*, …) are emitted with the sub-merchant's userId as their owner. If your marketplace listens for them, dispatch by userId to the right seller in your own database — the marketplace userId only appears on marketplace-fee-related events.

Common pitfalls

  • Forgetting the header from a marketplace key. You'll get 400 ON_BEHALF_REQUIRED_FOR_MARKETPLACE. The header isn't optional once your account is a marketplace — there's no fallback.
  • Reusing the marketplace's own user id in the header. Marketplaces are not sub-merchants; the request will fail with 404 ON_BEHALF_SUBMERCHANT_NOT_FOUND.
  • Acting on a not-yet-approved sub-merchant. Even right after POST /api/connect/accounts, you'll get 403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLE until KYC completes. Always check kycStatus = "approved" first.
  • Acting on a suspended sub-merchant. Same 403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLE. Resume them first via POST …/resume (see Suspension and Lifecycle).