An invoice payment is submitted at 16:00 UTC on Thursday. The rail’s cut-off is 15:00. The estimate shown to the customer says “arrives within 1 business day”.It arrives Tuesday. Nothing failed, nothing was retried, no error was logged, and the rail behaved exactly as documented.
The arithmetic nobody does
Missing a cut-off feels like being an hour late. It is not. The delay compounds in a way that is obvious once written down and invisible until then:1
You miss the window
16:00, cut-off was 15:00. One hour late.
2
The payment queues to the next opening
Not to 15:00 tomorrow: to the next time the rail opens. That is the following business morning.
3
Then it takes the rail's normal latency
The queue position is not a head start. The T+2 clock starts when the batch is picked up, not when the payment was submitted.
4
Weekends and holidays do not count
Thursday 16:00 → Friday’s batch → T+2 across a weekend → Tuesday.
Missing a batch rail’s cut-off by an hour costs a day: the queued payment starts from the next opening and then takes the rail’s normal latency. The two delays add; they do not overlap.
Sixty minutes of lateness produced ninety-six hours of delay. Every step is correct behaviour.
Why estimates are usually wrong here
The estimate said “within 1 business day”, which is the rail’s latency: its documented, headline number. Latency is what the rail publishes because latency is what the rail controls. The customer experiences submission to arrival, which is:How Arc models it
Rails carry their windows as data, not as a comment:
Two functions do the work:
isRailOpen returns true unconditionally for instant rails: they have no window, and special-casing that once means every caller downstream stops thinking about it.
nextSettlementTime does the compounding arithmetic properly: find the next opening, then add the rail’s latency, then skip non-business days.
That compounding is asserted by a test that checks the exact arithmetic, not assumed. A test that only checked “later than submission” would pass for an implementation that adds one hour, which is precisely the bug this exists to prevent.
The product decision this forces
Once the real arrival time is computable rather than estimated, a genuine choice appears, and it only appears because the arithmetic is honest.sepa_creditCheaper. Cut-off at 15:00. Fine for a payment with a week of runway.sepa_instantMore expensive, €100k cap, always open. Necessary for an invoice due tomorrow.nextSettlementTime is approximate cannot make the decision at all.
Arc currently maps currency to rail statically, with the cut-off arithmetic available but not yet driving selection. Wiring deadline-aware rail choice belongs with the transfer API. Stating it here rather than implying the capability exists.
The related trap: T+2 is not “two days”
eft is T+2. That is two settlement days, and the count starts from the settlement date, which is itself determined by the cut-off.
Submitted Friday at 15:00, one hour past the 14:00 cut-off:
The lesson
1
Business time is not wall-clock time
Any system touching banking rails needs a business-calendar concept as a first-class citizen. Retrofitting one later means auditing every date calculation in the codebase.
2
Delays compound; they do not overlap
“One hour late” and “one hour of delay” are different quantities. Assert the exact arithmetic, because a test checking “later than before” passes the naive implementation.
3
Show the customer arrival time, not rail latency
The published number is true and answers a question nobody asked. Compute what they actually experience.
4
Windows belong in data
A cut-off in a comment is a cut-off that will be wrong after the next daylight-saving change. In a table, it can be tested.
The settlement saga
Rails, cut-offs, caps and failure profiles in context.
Enterprise payout
Where this decision is worth the most: an invoice due Friday.