Building a decline dashboard: data, code groups and alerts
A decline dashboard starts with one record per payment attempt: the raw network response code, the provider's own code, the Mastercard merchant advice code, the 3-D Secure result, BIN, issuer and issuing country, provider, method, amount and whether it was a retry. Codes from every provider are mapped into a few decline groups, viewed over time, by country, provider and BIN, on intents and attempts, with alerts against your own baseline.
In 30 seconds
- Store the raw network code, the provider's code and the Mastercard advice code for every attempt; the provider's label alone is not enough.
- Map every provider's codes into a small set of decline groups, each with one default action, and review the mapping as codes change.
- Report on payment intents and on attempts side by side, and alert on changes against your own baseline rather than on industry averages.
- 9 sources cited
On this page8 sections
Every payment provider has a decline report. The trouble starts when there are two or three of them. One calls a decline “Refused”, another “do_not_honor”, a third “20005”. One counts a cascaded payment twice, another once. The monthly approval rate falls by two points and nobody can say whether it was one issuer, one country, one provider or a change in your own fraud rules.
This article explains how to build a decline dashboard that answers those questions: the data to capture for every attempt, how to normalise codes across providers, the views a payments team needs, where to set alerts and how to run a weekly review. It builds on our guides to calculating approval rate and soft and hard declines.
What data should you capture for every attempt?
One row per authorization attempt, linked to the payment intent it belongs to. If a field is not captured at the time, it cannot be recovered later.
| Field | Why it matters |
|---|---|
| Intent ID, attempt ID, attempt number | Separates purchases from retries and cascades |
| Retry or cascade flag, and what triggered it | Shows which rules created extra attempts |
| Provider and acquirer | Compares routes on the same traffic |
| Method, scheme, card type | Debit, credit and prepaid behave differently |
| BIN, issuer, issuing country | Finds issuer and cross-border patterns |
| Amount and currency | Limits and funds declines depend on them |
| 3-D Secure result | Separates authentication failures from issuer declines |
| Raw network response code | What the issuer actually sent |
| Provider’s own code and message | What your provider calls it |
| Mastercard merchant advice code | The issuer’s instruction for the next attempt |
| Where it failed | Your fraud rules, the provider’s risk checks, or the issuer |
Two fields are often missing. The raw code is not always passed through: Adyen, for example, returns it only once the merchant turns on the raw acquirer result setting, and its numeric refusalCodeRaw exists only for Visa and Mastercard payments on Adyen’s own acquiring. And the merchant advice code is dropped by many internal mappings. Our article on merchant advice codes explains why it should be kept.
The “where it failed” field matters as much as the code. Stripe separates issuer declines from blocked payments and invalid API calls, and a blocked payment shows a network status of not_sent_to_network. Checkout.com uses a separate 4xxxx range for risk responses. If your own blocks and issuer declines share one column, our article on false declines shows how that hides the problem.
How do you normalise codes across providers?
Map every code from every provider into a small set of groups, each with one default action. The raw network code is the anchor, because it is the same whichever provider carried the payment; the provider’s code is the fallback.
| Group | Visa / Mastercard raw codes | Stripe code | Adyen reason | Default action |
|---|---|---|---|---|
| Generic | 05 | do_not_honor, generic_decline | Refused | One retry on another route or with 3-D Secure |
| Funds and limits | 51, 61, 65 (Visa) | insufficient_funds, card_velocity_exceeded | Not enough balance, Withdrawal amount exceeded | Retry later or offer another method |
| Authentication | 1A (Visa), 65 Identity Check (Mastercard) | authentication_required | Authentication required | Retry with 3-D Secure |
| Card data | 54, N7, 14 | expired_card, incorrect_cvc, incorrect_number | Expired Card, CVC Declined, Invalid Card Number | Ask the customer to correct |
| Issuer fraud | 59 (Visa), 83 (Mastercard) | Read the network code | Issuer Suspected Fraud | Do not cascade; review |
| Card blocked | 04, 07, 41, 43, R0, R1, R3 | lost_card, stolen_card, pickup_card | Blocked Card, Revocation Of Auth | Stop; never retry |
| Technical | 91, 96 | issuer_not_available, processing_error, reenter_transaction | Issuer Unavailable, Acquirer Error | Fail over or retry shortly |
| Not sent to issuer | none | blocked outcome | FRAUD (Adyen risk) | Review your own and provider rules |
The code meanings come from Stripe’s lists of network and decline codes and Adyen’s refusal reasons. Checkout.com’s codes fit the same groups: 20005 do not honour, 20051 insufficient funds, 20059 suspected fraud, 20154 3DS authentication required and 30004 pick up card. Checkout.com also publishes which of its codes fall into each of Visa’s four response code categories, which is a useful check on your own mapping.
Three rules keep the mapping honest. Keep an “unmapped” group and review it weekly, because new codes appear. For Mastercard, let the advice code override the group’s default action. And version the mapping, since Adyen notes that schemes and issuers sometimes change the raw text without notice.
Which views does a payments team need?
Six views cover most questions. Each should be filterable by method, scheme, country and period.
| View | Shows | Question it answers |
|---|---|---|
| Declines by group over time | Daily share of attempts in each group | Did something change, and what kind of decline is it? |
| By issuing country | Approval and group mix per card country | Is a cross-border or domestic problem growing? |
| By provider, same segment | Groups per provider for the same country and method | Is one route worse on the same traffic? |
| By BIN and issuer | Top BINs by declined volume and generic share | Is one issuer refusing us in particular? |
| Intent versus attempt | Both rates on one chart | Are retries hiding or causing the change? |
| Retries and cascades | Recovery by group and advice code | Which retry rules earn their attempts? |
The intent versus attempt view is the one most teams lack. Stripe’s acceptance analytics show the gap: its raw rate counts every attempt, while its deduplicated rate groups retries of the same purchase and takes the final outcome. In Stripe’s own example, one purchase approved on the third attempt is a 33.3% raw rate and a 100% deduplicated rate. Checkout.com reports first attempt authorization success and the uplift from retries for the same reason.
Decide also what the denominator includes. Stripe’s authorization rate leaves out invalid API calls, failed 3-D Secure and blocked payments, because those fail before the issuer is asked; its payment success rate includes them. Show both, and label which one each chart uses.
For the BIN view, Checkout.com describes the issuing BIN as the first six to eight digits of the card number. Our article on BIN routing covers what to do once the view points to a single issuer.
Where should you set alerts?
On changes against your own baseline, not on industry averages. Decline rates depend on countries, card mix and customers, so a figure that is normal for one merchant is an incident for another.
- Technical group. Alert within the hour when the share of technical declines at one provider rises well above its median for the same hour of the week. This is usually an outage or a connection problem.
- Generic group by issuing country. Alert daily when one country’s generic share moves sharply against its four-week average.
- Unmapped codes. Alert when any new code appears more than a handful of times.
- Stop advice retried. Alert on any retry after MAC 03 or 21, or after a card blocked code.
- Volume. Alert on a sudden rise in attempts with a falling approval rate. Stripe notes that card testing can show as a lower rate and a spike in payment count.
Set each threshold as a setting, start loose, and tighten it once you know which alerts lead to action.
What does the weekly review look like?
Thirty minutes, the same agenda every week, with payments, risk and an engineer present.
- Intent-level approval against the previous four weeks, by top countries and methods.
- Group mix. Which decline group moved, and at which provider.
- Top ten BINs by declined volume. New entries and generic-share changes.
- Unmapped and new codes. Add each to the mapping or open a question with the provider.
- Retry and cascade recovery by group. Remove rules that recover almost nothing.
- Actions and owners. One line each, checked again next week.
Worked example: a weekly review at an electronics store
Example figures, for illustration only.
An online electronics store routes cards through two providers. This week it had 20,000 payment intents and 23,000 attempts, including retries and cascades. 16,400 intents were approved: an intent-level rate of 82.0% and an attempt-level rate of 16,400 ÷ 23,000 = 71.3%. The 6,600 declined attempts break down as follows.
| Decline group | Four-week weekly average | This week | Change |
|---|---|---|---|
| Generic | 2,350 | 2,400 | +50 |
| Funds and limits | 1,480 | 1,500 | +20 |
| Authentication | 620 | 600 | -20 |
| Card data | 510 | 500 | -10 |
| Issuer fraud | 290 | 300 | +10 |
| Card blocked | 410 | 400 | -10 |
| Technical | 310 | 900 | +590 |
| Total | 5,970 | 6,600 | +630 |
Only the technical group moved. Drilling into it by provider shows 780 of the 900 at provider B, in one three-hour window on Tuesday, which the hourly alert had already flagged. Cascading sent 700 of those payments to provider A, which approved 560. That is why the intent-level rate barely moved while the attempt-level rate fell: the customers mostly got through, at the cost of extra attempts.
The actions: ask provider B for an incident report, and check why 80 of its technical failures were not cascaded. Without the provider split and the intent versus attempt view, the team would have seen a lower attempt rate and started tuning fraud rules.
What are the common mistakes?
- Keeping only the provider’s label. Two providers can call the same issuer response by different names. Without the raw code, the comparison is unreliable.
- Mixing your blocks with issuer declines. Your own fraud rules and the provider’s risk checks need their own group, or a rule change looks like an issuer problem.
- One rate for everything. Attempt-level and intent-level numbers answer different questions; show both and say which a chart uses.
- Alerting on averages from elsewhere. Thresholds borrowed from another business fire too often or never. Use your own baseline.
- Letting the mapping go stale. New codes and changed texts pile up in “other” unless someone reviews them weekly.
- No owner for actions. A review that ends without names and dates is a meeting, not a routine.
How does Omno help build a decline dashboard?
Omno’s dashboard shows approval and decline reasons by provider, method, country and customer group, with real-time success metrics, and the routing-decision log records the eligible providers, the chosen one and every cascade attempt with its result for each payment. Because every connected provider reports through one data model, the views can compare routes on the same traffic. For custom views, such as BIN-level groups or your own code mapping, data export can feed your warehouse. The reporting and analytics page lists what is included.
Key facts
- Stripe says many card payments are categorised as generic declines, the most common being do_not_honor, and that issuers discuss specifics only with their cardholders (Stripe docs, 2026).
- Stripe's raw rate counts every attempt, while its deduplicated rate groups retries and uses the final outcome: one purchase approved on the third attempt is 33.3% raw and 100% deduplicated (Stripe docs, 2026).
- Stripe's authorization rate leaves invalid API calls, failed 3-D Secure and blocked payments out of the denominator, because those fail before the issuer is asked (Stripe docs, 2026).
- Checkout.com groups its response codes by range: 10xxx approved, 20xxx soft decline, 30xxx hard decline and 4xxxx risk responses (Checkout.com docs).
- Adyen returns a numeric raw code only for Visa and Mastercard payments on its own acquiring, and warns that schemes and issuers sometimes change the raw text without notice (Adyen docs, 2026).
- Checkout.com reports first attempt authorization success and the uplift from retries separately, with declines by issuing country, bank and BIN (Checkout.com docs).
Changelog
- First published.
Sources
9 references
- Declines, Stripe
- Acceptance analytics, Stripe
- Network decline codes, Stripe
- Stripe decline codes, Stripe
- Refusal reasons, Adyen
- Raw acquirer responses, Adyen
- API response codes, Checkout.com
- Authorization analytics, Checkout.com
- Visa response code categories, Checkout.com