April 11, 2026 9 min read

Alerting Strategies for Payment Systems — How We Cut Alert Noise by 90% Without Missing a Single Real Incident

Our on-call rotation was burning people out. Four hundred alerts a day, most of them noise, and the one that actually mattered got lost in the flood. Here's how we rebuilt our alerting from scratch and got it down to 38 alerts a day — every single one actionable.

The Alert Fatigue Problem

I'll be honest about where we were eighteen months ago: our payment platform was generating roughly 400 alerts per day across PagerDuty, Slack, and email. The on-call engineer's phone buzzed so often they started leaving it on silent. We had a running joke that the pager was basically a random number generator.

It wasn't funny when we missed a real incident.

A downstream acquirer started returning elevated timeout rates on a Saturday afternoon. The alert fired, but it was buried under 47 other notifications that had come in that hour — most of them CPU threshold breaches on hosts that were perfectly healthy under normal load spikes. By the time someone noticed the acquirer issue, we'd been silently failing transactions for 90 minutes. That was the wake-up call.

The core problem: When everything is urgent, nothing is. Alert fatigue doesn't just slow response times — it trains your team to ignore the pager entirely. In payment systems, that's a direct path to lost revenue and broken trust.

Symptom-Based vs. Cause-Based Alerts

The first thing we did was audit every single alert rule. We found that about 70% of our alerts were cause-based: CPU above 80%, memory above 90%, disk queue length above 10. These tell you something might be wrong with the infrastructure, but they don't tell you whether users are actually affected.

The shift to symptom-based alerting was the single biggest lever we pulled. Instead of alerting on "the database replica is lagging by 5 seconds," we alerted on "payment authorization p99 latency exceeds 2 seconds." The first one fires during every routine maintenance window. The second one only fires when customers are actually waiting.

Aspect Cause-Based (Old) Symptom-Based (New)
Example CPU > 80% for 5 min Error rate > 5% for 3 min
Signal Something might break Something is broken for users
False positive rate High — fires during normal spikes Low — tied to user impact
Actionability Often unclear what to do Clear: users are affected, investigate now
On-call burden Constant noise, alert fatigue Rare but meaningful pages

We didn't delete the cause-based alerts entirely. We demoted them to dashboards and low-priority Slack channels. They're still useful for debugging after you know there's a problem — they're just terrible at telling you a problem exists in the first place.

The Three-Tier Alert Framework

Not every problem deserves the same response. A complete payment gateway outage and a slightly elevated latency on a single endpoint are fundamentally different situations, but our old system treated them identically: page the on-call. We introduced a three-tier framework that routes alerts based on user impact and urgency.

P1
Page Immediately

Active user impact. Transaction failures, complete service outages, data integrity issues. Wake someone up at 3 AM. Examples: auth success rate drops below 95%, settlement file generation fails, duplicate charge detected.

P2
Slack Notify (Business Hours)

Degraded but functional. Elevated latency, single-provider errors, non-critical job delays. Needs attention today, not at 3 AM. Examples: p99 latency above 1.5s, one of three acquirers returning elevated errors, retry queue growing.

P3
Ticket / Dashboard Only

Informational. Capacity trends, non-urgent warnings, infrastructure metrics. Review during next working session. Examples: disk usage above 70%, certificate expiring in 14 days, replica lag above 2s.

The key insight was being ruthless about what qualifies as P1. We started with 60 P1 alerts and cut it down to 11. Every P1 had to pass a simple test: "If this fires at 3 AM, will the on-call engineer agree it was worth waking up for?" If the answer was "maybe" or "it depends," it wasn't a P1.

SLO-Based Alerting for Payments

The tier framework helped with routing, but we still had too many alerts firing. The real breakthrough came when we switched from static thresholds to SLO-based alerting with error budgets and burn rates.

The idea is simple: instead of alerting when error rate crosses some arbitrary line, you alert when you're consuming your error budget faster than you can sustain over the SLO window. A brief spike to 2% errors during a deploy is fine if your 30-day budget can absorb it. A sustained 0.8% error rate that looks "low" might actually burn through your budget in a week.

We defined our payment authorization SLO at 99.95% success rate over a rolling 30-day window. That gives us an error budget of 0.05% — roughly 720 failed transactions per day at our volume. The burn rate alert fires when we're consuming that budget at 6x the sustainable rate over a 1-hour window, or 3x over a 6-hour window.

Why burn rates beat static thresholds: A static "error rate > 1%" alert fires identically whether the spike lasts 30 seconds or 30 minutes. Burn rate alerts account for duration automatically — short spikes that self-resolve don't page, but sustained degradation that threatens your SLO does.

The results after three months spoke for themselves:

400 → 38
Alerts per day
12 → 3 min
Median response time
0
Real incidents missed

The 38 remaining daily alerts weren't all pages — most were P2 and P3 notifications flowing into Slack and our ticket system. The on-call engineer went from getting paged 15-20 times per shift to 1-2 times. And every single one of those pages was a genuine problem that needed human attention.

Alert Design Principles

Reducing the number of alerts was only half the battle. The alerts that remained needed to be good — meaning the on-call engineer could read one at 3 AM with half a brain and know exactly what was happening and what to do about it.

We established three rules for every alert definition:

Here's what a well-structured Prometheus alert rule looks like in practice:

# Good: symptom-based, SLO-aware, with full context
- alert: PaymentAuthBurnRateCritical
  expr: |
    (
      sum(rate(payment_auth_total{status=~"5.."}[1h]))
      / sum(rate(payment_auth_total[1h]))
    ) / 0.0005 > 6
  for: 5m
  labels:
    severity: page
    team: payments
    tier: p1
  annotations:
    summary: "Auth error budget burning at {{ $value | printf \"%.1f\" }}x sustainable rate"
    description: |
      Payment authorization errors are consuming the 30-day error
      budget at {{ $value | printf "%.1f" }}x the sustainable rate.
      At this pace, the budget will be exhausted in
      {{ printf "%.0f" (div 30 $value) }} days.
    impact: "Customers may be experiencing failed payment attempts"
    runbook: "https://wiki.internal/runbooks/payment-auth-burn-rate"
    dashboard: "https://grafana.internal/d/payment-slos"

Compare that to what we had before: alert: HighCPU, expr: cpu_usage > 80, labels: {severity: critical}. No context, no runbook, no indication of user impact. The on-call engineer would open that alert, stare at a CPU graph, shrug, and go back to sleep.

Five Rules We Live By

After eighteen months of iterating on our alerting strategy, these are the hard-won rules we've pinned to the wall in our team room. They sound obvious in hindsight, but every single one came from a painful lesson.

  1. Every page must be actionable right now. If the on-call engineer can't do something meaningful within 15 minutes of being paged, it's not a P1. We review every page in our weekly on-call retro and demote anything that didn't meet this bar.
  2. Alert on symptoms first, investigate causes second. Your customers don't care that your CPU is at 90%. They care that their payment failed. Alert on what the user experiences, then use dashboards and logs to find the root cause during triage.
  3. Treat alert rules as code. Our alert definitions live in version control, go through code review, and require a test case showing when they would and wouldn't fire. No more ad-hoc threshold changes in the Grafana UI at 2 AM.
  4. Delete alerts that nobody acts on. We run a monthly audit: if an alert fired more than 10 times in the past 30 days and was resolved without action every time, it gets deleted or reclassified. Unused alerts are worse than no alerts — they actively erode trust in the system.
  5. On-call should be boring. A quiet pager isn't a sign that monitoring is broken. It's a sign that monitoring is working. The goal is for the on-call engineer to spend their shift doing project work, not firefighting. If on-call is consistently stressful, your alerting strategy has a bug.

References

Disclaimer: This article reflects the author's personal experience and opinions. Product names, logos, and brands are property of their respective owners. Specific metrics, thresholds, and alert counts mentioned are illustrative — your values will depend on your transaction volume, infrastructure, and business requirements. Always verify with official documentation.