Contractor payouts
Pay contractors in stablecoins by giving each one a dedicated USD account that converts and delivers automatically.
Your customers are the people getting paid. Each one is verified once, gets a dedicated USD bank account, and receives stablecoins whenever the payer sends money to it.
The shape
Build order
| Step | Call | Notes |
|---|---|---|
| 1. Onboard the contractor | POST /api/customers with kind: "individual" and endorsements: ["endo_usd"] | Requesting endo_usd needs email and phone, already on the customer or in the same request. Send tax_id and tax_residence_country too: the review needs them |
| 2. Submit for verification | POST /api/customers/{customer_id}/verifications | The first submission. We hold documents until it, and a later PATCH that changes something resubmits by itself |
| 3. Create their wallet | POST /api/customers/{customer_id}/wallets | Needs an approved endorsement and your registered keys |
| 4. Create their account | POST /api/customers/{customer_id}/virtual_accounts with a wallet destination | {"kind": "wallet", "asset": "usdc", "wallet_id": "wal_..."} |
| 5. Show the deposit details | deposit_instructions from step 4 | Give them to the payer verbatim |
| 6. Let them cash out | POST /api/wallets/{wallet_id}/operations, then authorize | See Sending Funds |
Steps 3 and 4 are in that order deliberately: a wallet destination needs
the wallet to exist first.
Why a wallet destination rather than an address
You can deliver straight to an address the contractor gives you, which is simpler for a one-off payout. A held wallet is worth the extra setup when you want to:
- show a balance in your product, read from the chain rather than tracked by you;
- show history at
GET /api/wallets/{wallet_id}/history, including money the contractor received from anywhere else; - let them send without leaving your app.
If none of those apply, use {"kind": "address", ...} and skip the wallet
work entirely.
What to watch
endorsement.request_for_informationdrives most of your support load, because contractors submit unusable selfies. Build the fix-it screen from the requirement buckets before launch. See Verification needs more information.deposit.receivedtells you the payer paid.payout.completedcarries thetx_hashworth showing the contractor.wallet_transaction.created(withkind: "deposit") fires for money that arrived at the address without any payer of yours being involved. Contractors do get paid from elsewhere, and your ledger should not be surprised by it.
Fees
Set partner_fee_bps per account to price contractors individually. It
is charged in bps on each deposit, netted from the delivery, and credited
to your balance in real time. On the standard plan our own conversion fee
is metered to your monthly invoice, so it does not reduce the delivery.
See Fees & Quotes.
The failure you should design for
A payer is ready to pay before the contractor's verification is approved.
The account cannot exist yet, so there is nothing to send to. Handle it in
your product: publish deposit instructions only after
endorsement.approved fires, and until then tell the payer the contractor
is still onboarding.