Engineering

Why $100M is hundreds of thousands of payouts

Ask what a payments system needs to handle and the answer usually comes back in dollars. A hundred million a month. Half a billion a year.

That number sizes the business. It does not size the system, and on the corridors where this product operates the two are not even loosely related.

The ceiling is not yours to set

Every corridor declares its own methods, its own local currency, and its own minimum and maximum amount. Those limits are set by the operator and the local regulator, not by us and not by you.

On a bank rail the ceiling is high enough that it rarely governs anything. On mobile money it is low — a few hundred dollars in local currency for a single transfer, sometimes less, with a separate ceiling on what a wallet may hold or receive in a period.

So a large amount delivered over mobile money is not one payout. It is many, and the arithmetic is unforgiving: a hundred million dollars moving through a rail that caps around a few hundred is on the order of hundreds of thousands of individual transfers.

Same notional. Four orders of magnitude more operations.

Every limit you will hit is denominated in operations

This is the part that surprises people, and it is worth being blunt about, because nothing in a system is measured in dollars.

Rate limits are per request. Retry queues hold requests. Reconciliation compares rows. A status page renders a timeline per transaction. Support load scales with the number of recipients who can each individually be confused about where their money is. Storage, pagination, exports, alerting thresholds — all of it counts operations.

A system designed against a notional target and tested with large transfers will pass every test and then fall over on the first real payroll run, because the load it was built for and the load it received differ by a factor nobody wrote down.

What changes in the failure model

The interesting consequence is not throughput. Throughput is a capacity problem and capacity problems are solvable with ordinary engineering.

What changes is that partial failure becomes the normal state.

One wire either goes or it does not. Five thousand mobile-money transfers produce a distribution: most succeed, some fail on bad recipient details, a few sit in an unknown state because a connection dropped at the wrong moment, and a handful land after the others by hours. There is no run where everything succeeds. There does not need to be one for the system to be healthy.

So "did the batch work" stops being a question with an answer. The design has to be per-movement all the way down — per-movement state, per-movement retry, per-movement reconciliation — because there is no batch-level verdict that means anything useful.

It also means a human cannot be in the loop by default. At a few hundred payouts a day, somebody checks the exceptions. At a few hundred thousand nobody is checking, which is why the retry has to be safe by construction rather than safe by convention — the argument in idempotency keys and what they cannot save you from.

Small does not mean unimportant

There is a tempting inference here that small-ticket flow is low-value flow, and that a serious system should optimise for the large transfers.

It is the wrong way round. Small tickets are what wages, remittances, marketplace earnings and creator payouts look like in most of the world. The amount is small because the recipient is a person, not because the flow is marginal. A system that treats high-count, low-value movement as a degraded case of a wire is a system that works badly for the actual majority of cross-border payments.

The count is not an inconvenience on the way to the notional. For most of the world the count is the shape of the money.

What to ask instead

When you are sizing an integration, the useful question is not how much money will move. It is how many movements that will be, on which corridors, with which limits.

GET /v1/corridors returns the methods and the minimum and maximum for each one, which is enough to turn a notional target into an operation count before you have written anything. That number is what your rate limits, your queues, your reconciliation window and your support rota should be built against.

And it is why reconciliation has to run continuously rather than nightly — a day's worth of exceptions at this count arrives as a pile, not a list, as covered in reconciling payouts you don't control.

Corridor limits are in the docs. Questions: hi@spendfigo.com.

Common questions

Why does payout count matter more than payout volume?

Because every system limit is denominated in operations, not dollars. Rate limits, retry queues, webhook fan-out, reconciliation rows and support load all scale with the number of payouts. Notional tells you the business size; count tells you the engineering size.

Why are mobile money payouts capped at small amounts?

Per-transaction and per-wallet ceilings are set by the operator and the local regulator, and they are low relative to a bank wire. Large amounts are therefore delivered as many small transfers, which changes the failure model rather than just the arithmetic.

← All engineering writing