Multi-destination payments
A multi-destination payment order sends part of the payment to your wallet and the rest to the wallet of one of your partners. Both transfers happen in the same blockchain transaction, so neither side depends on the other settling first.
A partner does not need an account on Bitpayway. If a partner happens to be a Bitpayway business as well, the wallet you register for the link is used only for your multi-destination orders and never changes how that business receives its own payments.
Standard orders are unchanged. Omitting paymentModel keeps the existing
behaviour and the existing response shape.
How multi-destination appears in the API
In the API, multi-destination payments are called split. Whenever that name appears in a field or route, it refers to this feature:
| Where it appears | What it is |
|---|---|
paymentModel | Optional POST /api/order field that sets the order type. Send "split" to create a multi-destination order; without it, the order is a standard one. |
split object | The order's split details: the partner and their share. Sent when creating the order and returned in queries and webhooks. |
splitEnabled | Field of each token in GET /api/currency-token. true means the token accepts multi-destination payments. |
/api/split/... routes | Routes to register partners and multi-destination wallets, such as POST /api/split/partners and PATCH /api/split/wallet. The full list is in Configuring through the API. |
What the current version supports
| Supported | Not supported |
|---|---|
| One partner per order | Several partners in the same order |
| Allocation expressed as a gross amount | Allocation expressed as a percentage in the API |
| Partial payments and overpayments | Changing the allocation after the order is created |
If your interface offers a percentage, convert it to a gross amount before creating the order.
Wallet model and responsibilities
Multi-destination and standard payments use independent wallet registrations. Changing a multi-destination wallet does not change the destination of standard payments, and changing a standard settlement wallet does not affect multi-destination payments.
| Configuration | Scope | Where it is managed |
|---|---|---|
| Business multi-destination wallet | One business and token/provider; reused for every partner | Business dashboard or API |
| Partner link and wallet | One business-partner-token combination | API only |
| Standard settlement wallet | One business and token for standard payments | Business dashboard |
Your business's multi-destination wallet is configured for each token and its enabled multi-destination provider. You register it once and the registry reuses it for every partner linked to your business. Adding a partner does not require registering your business wallet again. Changing your business wallet changes the destination of your business's share for future multi-destination payments with every partner on that token.
A partner wallet is deliberately narrower: it belongs to one business-partner-token combination. If the same partner works with another business, that other link has its own wallet configuration. Partner links and wallets can only be created, changed, activated or deactivated through the API.
Both wallets and the partner link must be valid even when one share is zero. Neither destination wallet can be the payer of the payment transaction or the provider contract itself; your business and the partner may use the same destination address.
Configure multi-destination payments before the first order
Complete these three stages for every token you intend to accept:
- select a token with multi-destination support;
- register and enable your business's multi-destination wallet for that token; and
- create the partner link and register that partner's wallet.
Your business wallet and the partner wallet must both be registered on-chain before an order using that business-partner-token combination can be created.
1. Select a token with multi-destination support
The dashboard displays a wallet card for multi-destination payments only when
the token has an enabled multi-destination provider. A server integration can
discover the same availability with GET /api/currency-token: use a token whose
splitEnabled field is true and keep its contractId as the
tokenContractId used by the wallet routes.
Configuration is independent for each token. Repeat the remaining stages for USDT, USDC or any other token you want to use. If a provider later starts using a replacement wallet registry, register your business and partner wallets in that new registry as well.
2. Register your business's multi-destination wallet for the token in the dashboard
Open the business Configuration page and find Split settlement wallets. In the card for the selected token:
- enter the wallet that will receive your business's share;
- connect a browser wallet;
- select Update split wallet and approve the transaction; and
- wait for the card to report Enabled. Use Check status to refresh it.
This saves the wallet on the blockchain.
The wallet you enter does not need to be the same as the connected wallet you are using to perform this operation.
You can integrate with your application to perform this operation using
PATCH /api/split/wallet.
3. Create the partner and register its wallet through the API
Partner management is server-side and requires
Authorization: ApiKey <your-api-key>. First create the link with
POST /api/split/partners:
{
"name": "Marketplace partner",
"external_id": "marketplace-seller-42"
}
external_id is optional and accepts 1 to 255 characters. When set, it is
unique among your business's partners. Reusing it within the same business
returns 409; another business may use the same value. A partner created without it
returns "external_id": null.
Use the returned partner id to save and authorize its wallet for the selected
token with PATCH /api/split/partners/{partnerId}/wallet. Repeat the partner
wallet registration for every token that partner will use.
Setting a partner link to active:false and submitting its authorization blocks
new multi-destination orders and payments that use the link. Reactivation
requires another authorization. Changing either wallet affects future payments,
including payments toward orders that already exist.
Authorization and on-chain status
Every wallet change has two states: the configuration saved by Bitpayway and the state currently registered on-chain. Applying a change requires both saving the desired wallet and submitting the authorized registry transaction.
The dashboard automates both actions for your business's multi-destination wallet for that token after the user approves the browser-wallet transaction. When using the API—for either your business or a partner—your integration or relayer must submit the returned authorization. The API call itself does not spend gas or send the transaction.
The wallet PATCH response contains everything needed to send the transaction:
network, walletRegisterAddress, the registry abi, the functionName to
call and its args already in call order.
functionName is the name of the wallet registry contract method you must
call; it is part of the returned abi. Call that method on
walletRegisterAddress, on the network in network.chainId, passing args.
The value is setClientWalletWithAuth for your business's multi-destination
wallet for that token, and setSplitWalletWithAuth for a partner wallet and
active state; use the returned value instead of choosing it yourself.
The signed signatureParams remain available if you prefer to build the call
yourself, and expiresAt is the ISO 8601 form of signatureParams.deadline.
The submitting account is only a relayer. It pays gas but does not need to match either receiving wallet. The signature prevents it from changing the business, token, destination, partner state or deadline.
An authorization expires after ten minutes and can be consumed only once. If it
expires, is rejected or uses an already-consumed nonce, request a new one instead
of resubmitting it. Until the transaction is confirmed, the saved configuration
and the on-chain state differ and isEnabled remains false.
The status response separates those states: configured is the saved value,
while wallet and active are read from the blockchain. isEnabled:true means
they match. For a confirmed partner deactivation it can therefore be true
while active is false; that link still cannot receive new multi-destination
payments.
Configuring through the API
A server integration can configure your business wallet as an alternative to the
dashboard and must be used for all partner configuration. Send
Authorization: ApiKey <your-api-key>. The API key identifies the business, so
these routes do not accept a business ID:
| Operation | Route |
|---|---|
| Discover tokens | GET /api/currency-token |
| List partners | GET /api/split/partners |
| Create a partner | POST /api/split/partners |
| Save your multi-destination wallet and get an authorization | PATCH /api/split/wallet |
| Save a partner wallet and get an authorization | PATCH /api/split/partners/{partnerId}/wallet |
| Reissue the authorization for your wallet | POST /api/split/wallet/authorization |
| Reissue the authorization for a partner wallet | POST /api/split/partners/{partnerId}/wallet/authorization |
| Check the registry state of your wallet | GET /api/split/wallet/status |
| Check the registry state of a partner wallet | GET /api/split/partners/{partnerId}/wallet/status |
GET /api/split/partners accepts the optional exact-match filters id (the
public partner UUID) and external_id. The filters can be combined and never
return partners belonging to another business. For example:
GET /api/split/partners?external_id=marketplace-seller-42
GET /api/split/partners?id=9f1c2d64-5717-4562-b3fc-2c963f66afa6
Use the contractId of a token whose splitEnabled field is true as the
tokenContractId. Wallet requests have the following body:
{
"tokenContractId": "28a555aa-d6bb-4a0b-9266-0b6668f824b5",
"wallet": "0x1111111111111111111111111111111111111111",
"active": true
}
active applies to partner links and defaults to true. Omit it when
configuring your business's multi-destination wallet for the token, which cannot be
deactivated.
The PATCH response returns the registry address, ABI and signed parameters for
the saved configuration. Your integration or relayer must submit that
authorization to the registry before it expires, then poll the status route
until isEnabled is true. If the authorization expires or the transaction is
not submitted, send tokenContractId to the authorization route to reissue it.
The API does not spend gas or submit the blockchain transaction for you.
For ready-to-use ethers examples, see the optional technical reference Submitting wallet authorizations.
Creating an order
Add paymentModel and split to the existing POST /api/order request:
{
"requestId": "2f3c9d2c-1b75-4e9d-9b2f-67d0a9c3c7d1",
"description": "Order #1234",
"amount": "1000",
"network": "sepolia",
"token": "USDC",
"paymentModel": "split",
"split": {
"partnerId": "9f1c2d64-5717-4562-b3fc-2c963f66afa6",
"partnerAmount": "300"
}
}
partnerIdis the UUID returned byPOST /api/split/partners.partnerAmountis the partner's gross share, before the platform fee. It is a decimal string in the display unit of the token, between zero andamount, with at most the number of decimals the token supports.amountmust be greater than zero.
The order fixes the provider, the partner and the allocation when it is
created. Reuse the same requestId to repeat the same creation safely; the
existing order is returned. A 409 means that requestId was already used
with different data.
Reading an order
Multi-destination orders carry the following extra fields in GET /api/order,
GET /api/order/request/{requestId} and in webhooks:
| Field | Meaning |
|---|---|
paymentModel | split for multi-destination orders; absent on standard orders. |
split.partnerId | UUID of the partner link. |
split.partnerCodeId | On-chain identifier of the partner link. |
split.partnerAmount | Gross share agreed when the order was created. |
settlementStatus | pending, matched or mismatch. |
settlementVersion | Increases whenever the confirmed amounts change. |
The amounts are decimal strings in the order token:
| Field | Meaning |
|---|---|
paidAmount | Gross amount confirmed for the order. |
netAmount | Amount effectively received by you. |
partnerNetAmount | Amount effectively received by the partner. |
partnerGrossAmount | Gross amount accumulated for the partner. |
fee | Nominal total platform fee. |
clientCommission | Nominal fee charged on your share. |
partnerCommission | Nominal fee charged on the partner's share. |
commissionReceived | Amount effectively received by the platform. |
{
"order": {
"id": "7f7f5a2a-5c4f-4c6b-9cbb-1f0f4b4e42f2",
"date": "2026-02-12T18:12:43.384Z",
"requestId": "2f3c9d2c-1b75-4e9d-9b2f-67d0a9c3c7d1",
"clientId": "12345",
"orderId": "42",
"description": "Order #1234",
"amount": "1000",
"status": "partial paid",
"gatewayUrl": "https://pay.example.com/?id=7f7f5a2a-5c4f-4c6b-9cbb-1f0f4b4e42f2",
"paymentModel": "split",
"settlementStatus": "pending",
"settlementVersion": 1,
"split": {
"partnerId": "9f1c2d64-5717-4562-b3fc-2c963f66afa6",
"partnerCodeId": "7",
"partnerAmount": "300"
},
"paidAmount": "500",
"netAmount": "346.5",
"partnerNetAmount": "148.5",
"partnerGrossAmount": "150",
"fee": "5",
"clientCommission": "3.5",
"partnerCommission": "1.5",
"commissionReceived": "5"
}
}
Use netAmount and partnerNetAmount as the amounts actually received rather
than rebuilding them from the gross amount minus the fee.
Your business statistics count only your own share. Adding your share to the partner's does not produce your net revenue.
Fees and partial payments
The platform fee is charged on each contribution and split between you and the partner according to each share. A partial payment is divided in the same proportion as the whole order. See how the calculation works, with an example, in Fees.
Two payments quoted against the same confirmed total can differ by rounding and need reconciliation.
Confirmation and divergence
A successful transaction receipt is not an approved order. Bitpayway finalizes
a multi-destination payment only after twelve block confirmations.
Meanwhile, a payment that
completes the order with the agreed division leaves it
paid waiting confirmations, and a partial payment leaves the status unchanged.
The order accepts payments for 30 minutes after it is created.
The contracts let the payer choose the division that is submitted. Bitpayway detects a division that diverges from the agreed one after the transfers have already happened and does not approve such a payment automatically. The on-chain transfers cannot be reversed by the platform.
This is what settlementStatus reports:
pending: no confirmed divergence, and the order may still be settling.matched: the confirmed amounts follow the agreed allocation.mismatch: the confirmed amounts diverge and the order needs reconciliation with your partner.
Settlement is a financial dimension and does not replace the commercial status.
An order that was already approved stays paid if a later payment brings a
divergent division; it is flagged as mismatch, and its totals and
settlementVersion are updated. A canceled order stays canceled even if funds
arrive afterwards.
Webhooks
A multi-destination order sends the fields above inside the existing webhook notification, verified with the same HMAC signature. Two differences matter for multi-destination orders:
- A settlement change can produce a webhook after a terminal status, so an order can be notified more than once.
settlementVersionidentifies the version of the amounts. A response to an older notification does not confirm delivery of a newer one.
Handle repeated notifications idempotently and do not repeat a commercial fulfilment on every financial update. See the webhook guide for the request format and signature verification.