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.com/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.com/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.com/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": "EURC"
}'accountId references a payout destination (bank account or wallet) that already belongs to the sub-merchant. currency is the settlement currency to draw from (EURC or USDC). For crypto wallet destinations, also pass network (base, polygon, ethereum, solana, arbitrum, avalanche).
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.
Updated 23 days ago
