Part 6 of 7 · Dunning recovery series ~6 min read

What dunning recovery costs

This is one of the cheapest systems in the series, and the reason is worth stating plainly: the part that recovers the money — the retry schedule — is free, because it’s plain Python doing date arithmetic. The whole bill is a few small variable lines on top of one fixed cost. This post breaks down exactly where the $1.90 a month goes, and what changes when you run ten times the volume.

Key takeaways

  • About $1.90/month at ~150 active subscriptions and ~30 failed charges a month.
  • The biggest single line is Secrets Manager — three secrets at $0.40 — because the actual work is so cheap.
  • The part that recovers the money, the retry schedule, is free: it’s plain Python doing date arithmetic.
  • No always-on compute, no NAT Gateway, no API Gateway — the variable cost is pennies.
  • At ten times the volume it lands around $4.90, because the fixed Secrets Manager cost doesn’t grow.

The breakdown, line by line

The headline is almost funny: the single most expensive thing in a payment-recovery system is the place it keeps its passwords. At small-business volume — take ~150 active subscriptions, ~30 charges failing in a month, and around 75 dunning emails sent as those failures work through the retry schedule — here’s where every cent of the ~$1.90 goes.

Service What it does here Monthly
Secrets Manager3 secrets × $0.40 — processor API key, webhook secret, link-signing key$1.20
CloudWatch LogsAll Lambdas, structured JSON, 7-day retention$0.30
Bedrock (Haiku 4.5)One small copy call per dunning email + the monthly report$0.20
LambdaWebhook, scheduler, retrier, mailer, gatekeeper, sweep — all arm64, short$0.05
DynamoDB (on-demand)Dunning state, audit log, dedupe — a few hundred small writes$0.05
EventBridge SchedulerOne-off retry rules + the daily sweep (~100 invocations)$0.03
SES (outbound)~75 dunning emails at $0.10 per thousand$0.02
S3Email templates, logo, monthly report — a few MB$0.02
SQS (+ DLQ)Buffers webhook events — a few hundred messages$0.01
AWS BudgetsFirst two budgets are free$0.00
Total~$1.90

Where the dollars actually go

Secrets Manager (the bulk). Three secrets, each $0.40 a month, is $1.20 — almost two-thirds of the bill. That feels like a lot until you remember what they protect: the API key that can charge cards, the webhook secret that authenticates the processor, and the key that signs update-card links. Rotating them, scoping them per-Lambda, and keeping them out of environment variables is exactly where you want to spend a dollar. If you really wanted to trim it you could fold the link-signing key into Parameter Store (free for standard parameters), but the saving is $0.40 and the convenience of one secrets story isn’t worth losing.

Bedrock (one small call per email). Each dunning email is a few hundred tokens in and a few hundred out on Haiku 4.5 — a fraction of a cent. Across ~75 emails it’s well under twenty cents, and the monthly recovery report adds one slightly larger call. The retry schedule, the charge, and the pause all run with no model at all, so AI is a rounding error here, not a driver.

Lambda, DynamoDB, Scheduler, SES, SQS, S3 (pennies, together). This is the whole working machine, and it costs less than a dollar combined. The functions are short and arm64; the tables take a few hundred small writes; the one-off schedules fire ~100 times; the emails are $0.10 per thousand. None of it is rounding up to anything meaningful at this volume.

Monthly cost at base volume and ten times the volume, fixed versus variable A simple two-bar comparison of monthly cost in US dollars. The left bar represents base volume — about 150 subscriptions and 30 failed charges — with a total around $1.90. It is split into a large fixed slice for Secrets Manager (about $1.20) at the bottom and a smaller variable slice (about $0.70) on top for everything else: CloudWatch, Bedrock, Lambda, DynamoDB, Scheduler, SES, S3, and SQS. The right bar represents ten times the volume — about 1,500 subscriptions and 300 failed charges — with a total around $4.90. Its fixed Secrets Manager slice is the same height, about $1.20, because secrets cost the same regardless of volume; its variable slice has grown to about $3.70 because logs, Bedrock copy calls, and per-event work all scale with the number of failed charges. A legend marks the fixed slice and the variable slice. A note at the bottom reads: the fixed cost is flat, so ten times the work is far less than ten times the bill — and the retry schedule itself stays free. $0 $2 $4 $6 Base volume ~150 subs, ~30 fails ~$1.90 10× volume ~1,500 subs, ~300 fails ~$4.90 Fixed (Secrets Manager, ~$1.20 — same at any volume) Variable (everything else) The fixed cost is flat — ten times the work is far less than ten times the bill, and the retry schedule stays free.
Fig 6. Cost at base volume and ten times the volume. The Secrets Manager slice is the same height in both bars; only the variable slice grows. The retry schedule itself contributes nothing — it’s plain Python.

What doesn’t cost money

  • API Gateway. Replaced by Lambda Function URLs for the webhook receiver and the entitlement check.
  • NAT Gateway. Nothing runs in a VPC. No NAT, no $32/month minimum.
  • Always-on compute. No EC2, no Fargate. The system uses zero compute in the days between one customer’s retries.
  • A scheduler that’s always running. No cron table to poll. Each retry is a one-off EventBridge Scheduler rule that self-deletes after it fires.
  • Models on the money path. The schedule, the charge, and the pause are plain Python. Bedrock only writes email copy.

How the cost scales

At ten times the volume — around 1,500 subscriptions and 300 failed charges a month — the bill is about $4.90, not $19. The reason is the fixed/variable split in Fig 6: Secrets Manager stays at $1.20 no matter how many charges fail, while CloudWatch Logs, the per-email Bedrock calls, and the per-event Lambda and DynamoDB work scale with the number of failures. Logs tend to be the line that grows fastest, so structured-but-lean logging and the 7-day retention matter. Past a few thousand subscriptions you’d look at batching log delivery and at a provisioned-concurrency review — but those are tuning, not a redesign. The retry schedule never becomes the cost, because it never costs anything.

Set an AWS Budgets alarm at $10/month so anything unusual pages you before it matters. The recovered revenue dwarfs the bill at any volume: one saved £29 subscription pays for more than a year of running the whole system.

Last post in the series: the engineering reference. Same system, drawn for engineers — service names, the Lambda inventory, IAM scopes, DynamoDB schemas, the SES setup, the Bedrock model id, and the EventBridge Scheduler config.

All posts