Five metrics for a quote-based payout flow: realized slippage, expiry rate, re-quote delta, delivery success, and time to settle, with formulas and a log.
Payout quality comes down to five numbers per corridor: realized slippage, quote expiry rate, re-quote delta, delivery success rate, and time to settle. On a binding quote, realized slippage should be zero. The other four tell you whether your screen flow, your corridors, and your provider are doing what they promised.
Key takeaways
quote_id, is enough to compute all five.status field on each webhook, not by the event name.Because a quote-based flow can fail quietly. A user sees one number, confirms, and either gets that number or does not. If you only track volume and revenue, you will not notice a corridor where one confirmation in five expires, or a rail whose slow tail doubled last month.
The approach borrows from site reliability engineering. Google's SRE book frames service quality as indicators, objectives, and agreements: pick a few metrics users care about, set a target for each, and decide in advance what you do when you miss it. The five metrics below are those indicators for payouts.
They extend the three checks in how live quotes turn the quoted rate into the rate you get. Terms follow the on/off-ramp liquidity glossary.
Each metric has one formula, a starting target, and a meaning when it goes wrong. Targets below are starting points; set your own after a few weeks of data.
| Metric | Formula | Starting target | A bad number tells you |
|---|---|---|---|
| Realized slippage | (quoted receiver_amount - delivered amount) / quoted receiver_amount x 10,000 | 0 bps on a binding quote | The quote was not binding, or a fee landed outside the quote |
| Quote expiry rate | quotes expired before confirm / quotes shown | Under 10% on a single confirm screen | Quotes are fetched too early, or the flow is too long |
| Re-quote delta | (new receiver_amount - old receiver_amount) / old receiver_amount x 10,000 | Within the corridor's normal range | The corridor is volatile right now |
| Delivery success rate | completed / (completed + failed + refunded) | Above 99% on established corridors | Bad recipient data, compliance holds, or a rail issue |
| Time to settle | completed_at - executed_at, p50 and p90 per rail | Within the rail's expected window | A rail, a review step, or a cut-off is slowing payouts |
A few rules for reading them:
expires_at, the recipient gets the quoted receiver_amount. A non-zero value is not "a bit of noise", it is a finding.completed, failed, or refunded.The policy benchmark for speed is the G20 roadmap. The Financial Stability Board's targets aim for 75% of retail cross-border payments to make funds available within one hour of initiation, and the rest within one business day, by end-2027. Your p50 and p90 per rail show where you stand against that.
Illustrative example. A marketplace pays sellers in Brazil and Mexico. One week of data:
What to do with it. The 14% expiry rate is above the starting target, so look at the screen: the quote is probably fetched when the payout form opens, not when the user reaches the confirm step. Moving the quote request later is the cheapest fix. The delivery success rate is healthy; the 10 failures and refunds go to a weekly review of recipient bank details. Common stablecoin payout mistakes lists the usual causes.
One row per quote, keyed by quote_id, updated as the payout moves. This is enough to compute all five metrics.
| Field | Source | Used for |
|---|---|---|
quote_id | Quote response (qu_...) | Primary key |
requote_of | Your app: the expired quote_id this one replaced | Re-quote delta |
corridor | Your app: currency and rail | Grouping |
quoted_sender_amount, quoted_receiver_amount | Quote response sender_amount, receiver_amount | Slippage, re-quote delta |
commercial_quotation, blindpay_quotation | Quote response | Spread and cost tracking |
quote_created_at | Your clock when the response arrives | Expiry analysis |
expires_at | Quote response, epoch milliseconds | Expiry rate |
confirmed_at | Your clock when the user confirms | Expiry rate, time on screen |
payout_id, executed_at | Payout response (po_...) and your clock | Time to settle |
status | Latest payout webhook | Delivery success |
completed_at | Completion time from the payout's tracking objects | Time to settle |
delivered_amount | Recipient confirmation or bank statement, where you have it | Realized slippage |
A sample row:
Two traps. expires_at is epoch milliseconds, not seconds; divide by 1,000 only if your date library expects seconds. And amounts are integers in minor units, so 537300 means R$5,373.00.
Webhooks update the status and timestamps without polling. BlindPay sends three payout events, documented in the webhook events reference:
payout.new: the payout is created and the source funds are captured. Set payout_id and status.payout.update: the payout moves through an intermediate step, such as document checks, manual review, or the bank send. A payout that reaches failed arrives here too.payout.complete: the payout is confirmed at the destination or refunded.So the rule is simple: on every payout event, read the status field and write it to the row. Classify completed, failed, and refunded as terminal from the status, never from the event name alone. That keeps delivery success correct whichever event carried the change.
Verify every webhook signature before trusting it, and process events idempotently, since the same event can arrive twice. Stablecoin webhooks and reconciliation covers both, and stablecoin payout statuses explained covers what each status means for the recipient.
Three rows, read top to bottom in under a minute.
failed or refunded payout with its reason.Alert on two things only: any non-zero realized slippage, and a delivery success rate that drops below target for a corridor. Everything else is a weekly review.
When an expired quote triggers a re-quote in your code, log the link between the two ids. How to handle expired quotes in TypeScript and Python shows the retry pattern, and cost per delivered dollar turns the same log into an all-in cost per corridor. For who absorbs the market move while a quote is open, see who carries FX risk in a stablecoin payout.
Every BlindPay quote returns the fields the log needs: id, expires_at, commercial_quotation, blindpay_quotation, sender_amount, receiver_amount, and the fee lines. Payout quotes lock the rate and fees for 5 minutes by default, and an expired quote is rejected instead of executed at a new rate, so realized slippage on BlindPay payouts executed in time is designed to be zero.
Payout webhooks are signed and carry the payout's status and tracking objects. If a payout cannot be delivered after the stablecoins are collected, BlindPay refunds them to the quote's refund wallet, or to the sending wallet when none is set; on Pix and other non-USD local rails the refund is automatic, while USD payouts and payouts whose review times out are refunded after an operator review.
Add the event log table to your database, write one row per quote, and backfill a week of payouts. Then compute the five metrics per corridor and set your first targets. The payout quotes docs list every field the log reads.
This article is general information, not legal, tax, or financial advice.
Seven places blockchain payments beat bank rails: contractor payroll, remittances, B2B suppliers, marketplaces, 24/7 treasury, bill pay, and PSP payouts.
AP2, ACP, and x402 each verify that an AI agent had permission to spend. Here is what every protocol covers, who backs it, and the reconciliation gap none of them close.
The three roles behind every card program, stablecoin cards included: what each one does, who carries liability, and what to ask a provider.