Payout quality metrics: realized slippage, quote expiry rate, and re-quote delta

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

  • Realized slippage on a binding quote executed inside its window should be zero, every time.
  • Expiry rate measures your UX; re-quote delta measures the market; delivery success and time to settle measure the rails.
  • One event log row per quote, keyed by quote_id, is enough to compute all five.
  • Classify payout outcomes by the status field on each webhook, not by the event name.
  • Report time to settle as a median and a 90th percentile per rail. Averages hide the slow tail.

Why measure payout quality at all?

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.

Which five metrics matter, and how do you calculate them?

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.

MetricFormulaStarting targetA bad number tells you
Realized slippage(quoted receiver_amount - delivered amount) / quoted receiver_amount x 10,0000 bps on a binding quoteThe quote was not binding, or a fee landed outside the quote
Quote expiry ratequotes expired before confirm / quotes shownUnder 10% on a single confirm screenQuotes are fetched too early, or the flow is too long
Re-quote delta(new receiver_amount - old receiver_amount) / old receiver_amount x 10,000Within the corridor's normal rangeThe corridor is volatile right now
Delivery success ratecompleted / (completed + failed + refunded)Above 99% on established corridorsBad recipient data, compliance holds, or a rail issue
Time to settlecompleted_at - executed_at, p50 and p90 per railWithin the rail's expected windowA rail, a review step, or a cut-off is slowing payouts

A few rules for reading them:

  • Realized slippage is binary in practice. On a binding quote executed before expires_at, the recipient gets the quoted receiver_amount. A non-zero value is not "a bit of noise", it is a finding.
  • Re-quote delta needs the same inputs. Compare quotes with the same amount, currency side, fee setting, and destination. Otherwise you are measuring your own changes.
  • Delivery success excludes payouts still in flight. Count only payouts that reached a terminal status: completed, failed, or refunded.
  • Time to settle is per rail. Pix and SPEI settle in minutes, RTP is instant, and ACH, TED, and SEPA take about one to two business days, per BlindPay's cut-off times reference. Blending them makes every number meaningless.

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.

What does a week of numbers look like?

Illustrative example. A marketplace pays sellers in Brazil and Mexico. One week of data:

  • Quotes shown: 4,000. Confirmed and executed: 3,250. Expired before confirm: 560. Abandoned without expiring: 190.
  • Expiry rate: 560 / 4,000 = 14%.
  • Re-quotes after expiry: 480. Average re-quote delta: 9 bps on BRL, 6 bps on MXN.
  • Payouts finished: 3,240 completed, 6 failed, 4 refunded. Delivery success: 3,240 / 3,250 = 99.69%.
  • Realized slippage: 0 bps on all 3,240 completed payouts.
  • Time to settle over Pix: p50 3 minutes, p90 11 minutes.

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.

What should the event log store?

One row per quote, keyed by quote_id, updated as the payout moves. This is enough to compute all five metrics.

FieldSourceUsed for
quote_idQuote response (qu_...)Primary key
requote_ofYour app: the expired quote_id this one replacedRe-quote delta
corridorYour app: currency and railGrouping
quoted_sender_amount, quoted_receiver_amountQuote response sender_amount, receiver_amountSlippage, re-quote delta
commercial_quotation, blindpay_quotationQuote responseSpread and cost tracking
quote_created_atYour clock when the response arrivesExpiry analysis
expires_atQuote response, epoch millisecondsExpiry rate
confirmed_atYour clock when the user confirmsExpiry rate, time on screen
payout_id, executed_atPayout response (po_...) and your clockTime to settle
statusLatest payout webhookDelivery success
completed_atCompletion time from the payout's tracking objectsTime to settle
delivered_amountRecipient confirmation or bank statement, where you have itRealized slippage

A sample row:

JSON
{
  "quote_id": "qu_000000000000",
  "requote_of": null,
  "corridor": "BRL_pix",
  "quoted_sender_amount": 100000,
  "quoted_receiver_amount": 537300,
  "commercial_quotation": 5.4,
  "blindpay_quotation": 5.373,
  "quote_created_at": "2026-10-12T14:00:02Z",
  "expires_at": 1791813902000,
  "confirmed_at": "2026-10-12T14:01:15Z",
  "payout_id": "po_000000000000",
  "executed_at": "2026-10-12T14:01:16Z",
  "status": "completed",
  "completed_at": "2026-10-12T14:04:40Z",
  "delivered_amount": 537300
}

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.

How do webhooks feed the log?

Webhooks update the status and timestamps without polling. BlindPay sends three payout events, documented in the webhook events reference:

  1. payout.new: the payout is created and the source funds are captured. Set payout_id and status.
  2. 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.
  3. 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.

What should the dashboard show?

Three rows, read top to bottom in under a minute.

  • Row 1, health tiles. Five numbers for the last 7 days, each against its target: realized slippage (should read 0 bps), expiry rate, average re-quote delta, delivery success rate, and Pix or SPEI p90 time to settle.
  • Row 2, per-corridor trends. One line chart per metric, split by corridor, over 90 days. Expiry rate and re-quote delta on the same time axis, so you can see when volatility drove expiries.
  • Row 3, exceptions. A table of payouts that broke a rule: any non-zero realized slippage, any payout past its rail's p90, and every 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.

How does BlindPay help you measure this?

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.

What to do next

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.

FAQ