Collecting USDC is only half the story — most merchants want the money in their bank account, in local currency. Settlement is the process of converting your on-chain USDC balance to fiat and paying it out through a Stellar anchor using the SEP-24 interactive flow. This guide covers configuring anchors, initiating settlements from the dashboard and API, tracking their lifecycle, handling settlement webhooks, and meeting KYC requirements.
How settlement works
BettaPay batches your available USDC and hands it to an anchor. The anchor runs the off-ramp: it verifies your identity (KYC), converts USDC to fiat at its quoted rate, and deposits to your bank account. The SEP-24 standard defines this interactive handoff, and BettaPay tracks the transaction to completion on your behalf.
Supported anchors
Available anchors depend on your settlement country and currency. The table below shows current coverage. Availability changes as anchors complete integration.
| Anchor | Region | Currency | Rails | Status |
|---|---|---|---|---|
| Cowrie | Nigeria | NGN | Bank transfer (NIP) | Available |
| Fonbnk | Kenya | KES | M-Pesa, bank | Available |
| Link Money | Ghana | GHS | Mobile money, bank | In development |
| MoneyGram Access | Global | USD | Cash pickup | In development |
Configure settlement in merchant settings
Under Settings → Settlement, configure:
- Settlement anchor — pick from the anchors available in your region.
- Payout destination — your bank account or mobile money number. The anchor collects and stores these details during KYC.
- Schedule — manual (you trigger each settlement) or automatic (settle whenever the balance crosses a threshold).
- Minimum amount — BettaPay enforces a per-merchant minimum and maximum per settlement; balances below the minimum roll over to the next run.
Initiate a settlement
From the dashboard
Open Settlements → New settlement, review the available balance and the anchor's quoted rate and fee, then confirm. If the anchor needs interactive KYC or a one-time confirmation, a SEP-24 window opens to complete it.
From the API
Send an Idempotency-Key so a retried request never creates a duplicate payout. Amounts are strings to avoid floating-point errors.
const { apiRequest } = require("./bettapay-client"); // from the S2S guide
/**
* Initiate a settlement of USDC to fiat via the configured anchor.
* Idempotent on `batchRef` so retries are safe.
*/
async function createSettlement({ amount, anchorId, batchRef }) {
try {
const settlement = await apiRequest("/v1/settlements", {
method: "POST",
idempotencyKey: `settle-${batchRef}`,
body: {
amount: String(amount), // USDC to settle
asset: "USDC",
anchorId, // from Settings -> Settlement
reference: batchRef, // your own batch id
},
});
// { id, status: "PENDING", amount, fee, quotedRate, anchorId, ... }
return settlement;
} catch (err) {
// Per-merchant limits and insufficient balance surface as 4xx here.
console.error("settlement failed:", err.message);
throw err;
}
}
module.exports = { createSettlement };422 describing the allowed range — split the amount across runs.Track settlement status
Every settlement moves through a predictable lifecycle:
| Status | Meaning | Terminal? |
|---|---|---|
| PENDING | Accepted and queued for the next batch. | No |
| PROCESSING | Handed to the anchor; KYC/conversion/payout in progress. | No |
| COMPLETED | Fiat delivered to your payout destination. | Yes |
| FAILED | Anchor rejected or payout failed; USDC returned to balance. | Yes |
Fetch the current status any time:
const { apiRequest } = require("./bettapay-client");
async function getSettlement(settlementId) {
const s = await apiRequest(`/v1/settlements/${settlementId}`);
// { id, status, amount, fee, netFiatAmount, currency, anchorRef, completedAt }
return s;
}
module.exports = { getSettlement };PROCESSING for minutes to hours depending on the anchor and banking rails — polling that whole time is wasteful.Handle settlement webhooks
BettaPay fires an event on each state change. Verify the signature and dedupe exactly as in Webhook Handling & Retry Logic, then react to the settlement types.
function onSettlementEvent(event) {
switch (event.type) {
case "settlement.processing":
markInProgress(event.data.settlementId);
break;
case "settlement.completed":
// Record the NET fiat amount actually delivered, not the gross USDC.
recordPayout({
id: event.data.settlementId,
net: event.data.netFiatAmount,
currency: event.data.currency,
anchorRef: event.data.anchorRef,
});
break;
case "settlement.failed":
// USDC has been returned to your balance; surface the reason.
alertFinanceTeam(event.data.settlementId, event.data.failureReason);
break;
default:
break;
}
}A completed settlement webhook looks like this:
{
"id": "evt_8b21c0aa",
"type": "settlement.completed",
"createdAt": "2026-07-19T14:32:10Z",
"data": {
"settlementId": "stl_44f0c9",
"status": "COMPLETED",
"amount": "500.00",
"asset": "USDC",
"netFiatAmount": "747250.00",
"currency": "NGN",
"fee": "2.50",
"quotedRate": "1502.51",
"anchorId": "cowrie",
"anchorRef": "CWR-9F31A",
"reference": "batch-2026-07-19"
}
}Compliance and KYC
Off-ramping to fiat is regulated. Before your first settlement completes, the anchor must verify your identity:
- Business or individual KYC — legal name, address, and government ID or business registration, collected through the anchor's SEP-24 interactive flow.
- Bank account verification — the payout destination is validated and, for some anchors, name-matched against your KYC.
- Limits tiers — higher settlement limits may require additional documentation. Until KYC is approved, settlements stay in
PROCESSINGor are rejected.
Troubleshooting
Settlement stuck in PROCESSING
Usually pending KYC or a banking cut-off window. Check the anchor reference (anchorRef) in the settlement detail and, if the anchor requested more documents, complete them in the SEP-24 window.
Settlement FAILED and balance returned
The anchor rejected the payout — commonly a bank detail mismatch or a limit breach. Read failureReason, correct your payout destination in settings, and re-initiate.
"Amount exceeds settlement limit"
You requested more than your per-merchant maximum. Split the amount across multiple settlements, or contact support to raise your tier (which may require additional KYC).
Next steps
- Make settlement webhooks reliable → Webhook Handling & Retry Logic.
- Practice the flow safely → Testing with Stellar Testnet.
- Settlement endpoint details → API reference.