Acting on Behalf of a Sub-merchant
A marketplace's API key has two personalities:
- The marketplace itself — used to call the Connect-management routes (
/api/connect/*). - 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 headerX-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:
- The calling marketplace's Connect access is active (not paused, not disabled).
- The target sub-merchant exists and belongs to the calling marketplace — you can't borrow another marketplace's seller.
- 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": "…"
}| HTTP | errorCode | When |
|---|---|---|
400 | ON_BEHALF_REQUIRED_FOR_MARKETPLACE | Marketplace caller hit a merchant route without the header. |
403 | ON_BEHALF_FORBIDDEN_CALLER_TYPE | A non-marketplace caller (regular merchant) tried to use the header. |
404 | ON_BEHALF_SUBMERCHANT_NOT_FOUND | The id in the header doesn't correspond to a sub-merchant. |
403 | ON_BEHALF_SUBMERCHANT_NOT_OWNED | The sub-merchant exists but belongs to another marketplace. |
403 | ON_BEHALF_SUBMERCHANT_NOT_OPERABLE | KYC not approved, or the sub-merchant is currently suspended. |
403 | ON_BEHALF_MARKETPLACE_PAUSED | The calling marketplace's Connect access is currently paused. |
403 | ON_BEHALF_CONNECT_DISABLED | Connect 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 checkout payment for usr_seller_42
usr_seller_42curl -X POST https://api.inflowpay.xyz/api/payment \
-H "X-Inflow-Api-Key: $INFLOW_API_KEY" \
-H "X-On-Behalf-Of: usr_seller_42" \
-H "Content-Type: application/json" \
-d '{
"currency": "EUR",
"successUrl": "https://shop.example.com/success",
"cancelUrl": "https://shop.example.com/cancel",
"products": [
{ "name": "Hoodie", "price": 1500, "quantity": 1 }
]
}'Product price is in cents; the total is computed from products. The response returns { paymentId, purchaseUrl } — redirect the buyer to purchaseUrl (or embed it via the SDK iframe).
List a sub-merchant's customers
curl "https://api.inflowpay.xyz/api/customer?page=1&limit=20" \
-H "X-Inflow-Api-Key: $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/payout \
-H "X-Inflow-Api-Key: $INFLOW_API_KEY" \
-H "X-On-Behalf-Of: usr_seller_42" \
-H "Content-Type: application/json" \
-d '{
"accountId": "bank_1234567890abcdef",
"amountInCents": 50000,
"currency": "USDC",
"network": "base"
}'accountId references a payout destination (bank or wallet) that already belongs to the sub-merchant. currency is one of USDC or EURC. network is required only when the destination is a crypto wallet (avalanche, base, polygon, ethereum, solana, arbitrum).
Webhooks
Register webhooks on your marketplace API key (no X-On-Behalf-Of). Subscribe to connect.* event types — for example connect.payment.authorized and connect.payment.settled.
When a sub-merchant has activity, Inflow delivers a Connect event to your marketplace endpoint:
{
"object": "connect_event",
"eventType": "payment.authorized",
"subMerchant": { "id": "usr_…", "merchantName": "Seller Alpha" },
"data": { }
}The Svix delivery uses the prefixed name in data[].eventType (e.g. connect.payment.authorized). Use subMerchant.id to route to the correct seller in your system.
See Webhooks overview and Verifying signatures.
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 get403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLEuntil KYC completes. PollGET /api/connect/accounts/:subMerchantId/kyc/statusuntilkycReadyistrue. - Acting on a suspended sub-merchant. Same
403 ON_BEHALF_SUBMERCHANT_NOT_OPERABLE. Resume them first viaPOST /api/connect/accounts/:subMerchantId/resume(see Suspension and Lifecycle). - Using
Authorization: Bearer …. Inflow doesn't use bearer tokens for the public API — always sendX-Inflow-Api-Key. See Authentication & API Keys.