Skip to main content
A conversion and a fiat payout both let you declare your own margin on the request, and Conduit collects it for you. A deposit does not, and the reason is structural: you don’t start a deposit. Someone sends money to your customer’s account. There is no request of yours to put a margin on, so no deposit endpoint takes one. There are two ways to charge on money coming in. Which one you use depends on what the deposit is for.
Test both in sandbox. Simulate an inbound deposit and drive the follow-on payout with real webhooks — see deposit simulation and the cheat sheet.

If the deposit funds a conversion, take the margin on the order

When money arrives to be converted, you create the order, so it is yours to price. That order takes markupBps and markupAmount like any other, and Conduit collects your margin, accrues it when the order succeeds, and pays it on the monthly statement — exactly as Take your own margin describes. Prefer this route whenever it fits. It costs you one field. How you name the source depends on the asset. Crypto that has not arrived yet. Omit source and send sourceAsset instead; Conduit answers with a funding address and the order executes once the funds clear:
Read Deposit-funded orders before you build on this; funds that no order claims are returned to the sender. Fiat. sourceAsset is for crypto only — a fiat code there returns 400 VALIDATION_ERROR. Let the wire land in the customer’s virtual account first, then create the order naming that account, with the same margin fields:
Size amount off the deposit’s destination.assetAmount — what was credited — and not off what the sender wired. On a source-locked order totalDebit equals amount, so an order sized at the wire figure is never covered once a deposit fee comes out, and it sits pending until lockExpiresAt passes and it goes cancelled with cancellationReason: "expired".

If the deposit only credits a balance, sweep your fee

A deposit that simply credits your customer’s account has no order attached, so there is nothing to price. To charge here you collect the fee yourself: you move it out of the customer’s balance into an account you control. This is a different money model from the rest of the feature, and it is worth being explicit about the difference: A swept fee is your own money the moment it lands. It never appears on a markup balance and never on a markup statement.

Set it up once

  1. Onboard a customer in your own organization to receive the fees. It is an ordinary customer that you control rather than one of your end users — your fee-collection account. Give it a virtual account in the currency you collect in.
  2. Agree the payout purpose with Conduit before you go live. What a payout needs to clear follows from its purpose, and a recurring fee sweep is not the same case as the examples in Transact. Ask your Conduit contact which value to send.
  3. Then satisfy what that purpose asks for. Only intercompany uses the whitelist: the recipient must be pre-registered and reach registered first, through an asynchronous review that ends in whitelist_recipient.registered. That path is built for accounts inside the paying customer’s own group and is evidenced as such, so confirm with Conduit that it fits before you build on it. Most other purposes take a supporting document on each payout instead. prefunding is the exception that takes neither, and it is not a route for this sweep: its source must be a house account Conduit has marked as one, so it moves money to your customers rather than collecting from them. See Whitelist recipients and Transact.

Then sweep

Listen for transaction.completed with type: "deposit". Compute your fee off destination.assetAmount — the amount actually credited to the customer, after Conduit’s own fee — never off source.assetAmount. Then send a payout for it from that customer to your fee-collection account:
recipient carries the bank details of your fee-collection account’s virtual account — the full shape is in Send a payout.
Derive the idempotency-key from what you are sweeping, and persist it. A webhook can be delivered more than once, and your own process can crash between sending the payout and recording that it was sent. A fresh key on each delivery turns both into a second sweep, and your customer pays the fee twice; a stable key makes the replay return the one payout that was accepted. Verify the webhook signature before you act on it, too — see Webhooks.Key it to every dimension a batch has, as above: the source virtual account, the asset and the period. One payout debits one virtual account, so a key scoped to the customer and month alone collides the moment that customer is swept in a second currency — and that request either conflicts or replays the first sweep. Sweeping one deposit at a time? Use its transaction id.
When the recipient resolves to a Conduit-issued account in your own organization, the payout is delivered internally — no wire leaves Conduit. Your fee-collection account receives it as a deposit whose source carries type: "internal_transfer" and an originatingTransactionId naming the payout that sent it, and with no wire references at all. That link is how you reconcile a swept fee back to the deposit that earned it — fetch the payout it names for the sending side. clientReferenceId is your own reference on the payout, so it identifies a sweep in your records and on every read of that payout. It does not travel to the receiving deposit, which is not client-initiated and carries no reference of yours. On the wire path nothing on that deposit points back at the payout, so match it on amount and arrival.
Internal delivery is not guaranteed — it is what Conduit does when it can recognize the destination, and it falls back to an ordinary wire whenever it cannot. It needs at least a US-rail recipient whose routing and account numbers name exactly one active issued account, in the payout’s own currency, belonging to a different customer — one customer’s own two accounts never settle internally, which is why the fee-collection account above is its own customer. A SWIFT or SEPA recipient never qualifies. Treat the wire path as the one you must handle. A wired deposit carries no originatingTransactionId, and its source reads external_unknown until the sending bank’s details arrive, then external_bank_inbound.
Sweep in batches. One payout covering a day’s or a week’s deposits costs one payout instead of one per deposit, and one clientReferenceId then names the whole batch on your side. Sweeping per deposit is supported; it is rarely worth it.
You can declare a margin on the sweep payout itself, but there is usually no reason to: the whole payout is already your fee, and a margin on it would be Conduit collecting a second charge from your customer on top.

Crypto deposits

Crypto that lands in a customer’s wallet and stays there cannot be swept the same way. Moving it out is a crypto payout, and under the default custody model that needs that customer’s signers to approve each one — which is also why a crypto payout takes no margin field at all. So on crypto, charge where you have a request of your own: convert the deposit and take the margin on the conversion, as in the first section of this page.

See also