Stablecoin payroll
Pay 1099 contractors, international employees, and creator-economy collaborators in USDC. The recipient gets local fiat in their bank account (or holds USDC on their preferred chain) — T+0, 30bps fee, no correspondent banking delays.
Why stablecoin payroll
Compared to legacy options:
| Method | Cost | Time | Coverage | |---|---|---|---| | Wise / Remitly | 0.6–1.5% | 1-3 days | Limited corridors | | SWIFT directly | $25-50 + 2-4% | 2-5 days | Global but slow | | Soren Pay stablecoin payroll | 30bps | T+0 | 10+ corridors live, more via our issuing partner's Payments API |
Two flavors
A. Recipient gets local fiat
Use /api/payments/international — you fund in USDC; our issuing partner converts and pushes via the
local rail (SEPA, FPS, ACH, SWIFT, local LatAm/APAC).
B. Recipient gets USDC
Use /api/v1/checkout/sessions with settlementOption: "hold_stablecoin" —
the recipient picks their chain (Ethereum, Polygon, Arbitrum, Base, Solana)
and receives USDC directly to a wallet they control.
Step-by-step (recipient gets local fiat)
1. Collect bank details (one-time)
Build a simple form for each contractor:
- Name (legal)
- Country
- IBAN / account number / routing
- Currency they want to receive
Store in your DB. We don't host counterparty data for you yet (that's a future product).
2. Quote the conversion
const quote = await fetch("https://api.sorenpay.com/api/payments/international/quote", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SOREN_API_KEY}` },
body: JSON.stringify({
sourceAsset: "USDC",
amountUsdCents: 250000, // $2,500
destinationCurrency: "EUR",
destinationCountry: "DE",
}),
}).then(r => r.json());
console.log(`Recipient gets EUR ${quote.quote.destination_amount / 100}`);
console.log(`Fee: $${quote.quote.fee / 100}`);
console.log(`Settles via ${quote.quote.rail} by ${quote.quote.estimated_settlement}`);3. Originate the payment
const payment = await fetch("https://api.sorenpay.com/api/payments/international", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOREN_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
sourceAsset: "USDC",
sourceChain: "polygon",
amountUsdCents: 250000,
destinationCurrency: "EUR",
destinationCountry: "DE",
beneficiary: {
name: "Anna Müller",
iban: "DE89370400440532013000",
countryCode: "DE",
},
description: "May invoice — design contract",
}),
}).then(r => r.json());4. Watch for settlement
Subscribe to intl_payment.rail_settled and intl_payment.failed:
// Inside your webhook handler
if (event.type === "intl_payment.rail_settled") {
await markInvoicePaid(event.payload.id, event.payload.destination_amount);
} else if (event.type === "intl_payment.failed") {
// Hold is automatically reversed; refund the source UI
await markInvoiceFailed(event.payload.id, event.payload.failure_reason);
}Bulk payroll (script + CSV)
For monthly payroll runs, loop over a CSV:
import fs from "fs";
import { parse } from "csv-parse/sync";
const rows = parse(fs.readFileSync("payroll-may.csv"), { columns: true });
for (const row of rows) {
await fetch("https://api.sorenpay.com/api/payments/international", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SOREN_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `payroll-may-${row.contractor_id}`, // deterministic per row
},
body: JSON.stringify({
sourceAsset: "USDC",
sourceChain: "polygon",
amountUsdCents: Number(row.amount_cents),
destinationCurrency: row.currency,
destinationCountry: row.country,
beneficiary: {
name: row.name,
iban: row.iban,
countryCode: row.country,
},
description: `Payroll ${row.period} — ${row.notes}`,
}),
});
}Idempotency keys per (period, contractor) make re-runs safe.
Compliance notes
- Travel Rule: transfers > $3,000 USDC require originator + beneficiary info exchange. Handled automatically end-to-end. We don't surface a separate Travel Rule API yet — it's transparent to you.
- W-8 / 1099-NEC: still your responsibility. Soren Pay's ledger is the
source of truth for amounts paid; export from
/dashboard/treasury. - OFAC screening: our banking partner + Persona both screen at onboarding; our issuing partner re-screens at each Payments API call.
Related
- International payments use-case
- API reference: intl payments
- Compliance: identity — who verifies whom