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

quote.tstypescript
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

pay.tstypescript
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:

payroll-bulk.tstypescript
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