Use CasesB2B Invoicing & Settlement

B2B invoicing and settlement

Onboard businesses through KYB, give each one a receiving account, and settle invoices on chain.

Your customers are businesses. Their buyers wire the virtual account, and USDC lands in the seller's treasury address.

The shape

Build order

StepCallNotes
1. Onboard the businessPOST /api/customers with kind: "business"legal_name is required, plus endorsements: ["endo_usd"]
2. Add the people behind itcontrollers on the same callOne array with role flags, not three parallel lists
3. Submit for KYBPOST /api/customers/{id}/verifications/submitBusinesses owe a registration document
4. Create the accountPOST /api/customers/{id}/virtual_accounts with "rail": "wire"Wire carries a far higher per-transaction ceiling than ACH
5. Put the details on the invoicedeposit_instructions from step 4account_number, routing_number, account_holder_name
6. ReconcileGET /api/transactions, tx_hash on each payoutSee Reconciliation

Controllers, not three lists

The same human is very often a beneficial owner, a director, and the authorized signer. Parallel arrays force you to send them three times and keep three copies in sync, so there is one array with flags:

"controllers": [
  {
    "first_name": "Max",
    "last_name": "Muster",
    "date_of_birth": "1981-07-02",
    "has_ownership": true,
    "ownership_percentage": 60,
    "has_control": true,
    "is_signer": true,
    "role": "ceo",
    "id_document": {"kind": "passport", "number": "C9876"}
  },
  {"first_name": "Erika", "last_name": "Muster", "is_director": true}
]

ownership_percentage is required whenever has_ownership is set: an owner without a declared stake cannot be risk assessed. Anything downstream that wants separate lists gets them projected from this one, which is our problem rather than yours.

Wire against ACH

ACHWire
Per-transaction limitup to 1,000,000up to 20,000,000
Typical useRecurring, lower valueInvoice settlement, higher value
rail value"ach""wire"

Both rails hit the same account number. The rail you choose at creation names the primary one, and the response exposes routing_number_ach and routing_number_wire where the bank differentiates them. Put the right pair on the invoice.

You cannot create a second account in the same currency for the same customer: that returns 400 virtual_account_exists, because it is the same underlying account. PATCH the existing one's destination instead.

Matching payments to invoices

remittance_info carries what the sender typed, and sender_name carries who they are. Neither is guaranteed to be clean, so do not build your only matching path on them.

The reliable identifier is the account itself. One virtual account per customer means every arrival on it belongs to that customer, and network_reference on the deposit is the bank network's own end-to-end id, which is what a bank can trace when a buyer says the money left days ago.

What to watch

  • deposit.on_hold needs a human. A held payment on a large invoice is worth paging someone.
  • deposit.returned means money moved and came back. Reconcile it as two movements, and never auto-resend.
  • KYB takes longer than KYC. Do not build a flow where the buyer is waiting on the seller's verification: onboard the seller before you let them issue an invoice.