ALL PROJECTS/CROSS-BORDER REMITTANCE CONSOLE

Overview

A remittance operator needed an internal console for staff to move funds across a corridor on a customer's behalf: confirm the receiving account is real, check what the money is worth today, capture both sides of the transfer, send it, and be able to answer "where is it?" afterwards.

The work was a client engagement I led as architect. I owned the system design, the integration contract with the transfer provider, the data model and the operational posture; a two-developer build team implemented it over a run that started in March 2024 and had its last change in May 2025.

The interesting part is not the screens. It is that every decision in a money-movement tool is really a decision about what happens when something fails halfway — and the shape of this one is set by three of those.

Problem

A quoted rate is a promise with an expiry. The provider returns an exchange rate together with a token, and that token is what makes the rate binding. Quote a rate, let an operator spend four minutes typing beneficiary details, and send the transfer against a stale token, and you have either a rejected transfer or a transfer at a rate nobody agreed to. The window has to be a first-class thing in the design, not a comment.

A record written after the fact is a record you do not have. If the transfer call is made first and the row is written afterwards, then any failure between the two — a timeout, a crash, a dropped connection — produces money that may have moved with nothing on your side saying so. In this domain that is the worst outcome available, and it is worse than refusing to send.

The provider's contract is not uniform. Integrating with a live transfer API means accommodating what it actually does rather than what its documentation implies: requests that must be signed and timestamped, a header whose capitalisation is not the same on every operation, response codes where success and "accepted, still working" are different numbers, and a status vocabulary that has to be translated before an operator can act on it.

System Design

How It Works

01/04
01

A quote becomes a commitment

Why the rate is treated as perishable from the moment it arrives.

  1. 1An amount is entered and sent to the provider for a rate
  2. 2A rate comes back together with a token that makes it binding
  3. 3Both are held for the rest of the session, with the token's expiry
  4. 4The eventual transfer is sent against that token, or refused if it has lapsed
02

An account is confirmed before anything moves

Catching the most common error at the first step rather than the last.

  1. 1The receiving institution is chosen from a loaded list
  2. 2The account number is sent to the provider to be checked
  3. 3The provider returns the name held on that account
  4. 4The operator confirms the right person before any details are captured
03

The record is written, then the money is sent

The ordering decision the whole system is built around.

  1. 1Both sides of the transfer are captured and checked for completeness
  2. 2A transaction row is created in a deliberately unfinished state
  3. 3Only then is the transfer composed and sent to the provider
  4. 4The returned status, timestamps and provider reference are written back onto that row
04

The answer to "where is it?"

Two questions that deserve two paths rather than one overloaded screen.

  1. 1A single transfer is looked up by its own reference
  2. 2A period is pulled back as a date-ranged history
  3. 3A single lookup's status is returned as a plain outcome; a period comes back as a status-coded table
  4. 4One question is about a payment, the other about a day's activity

Key Features

  • A four-stage transfer workflow — validate, quote, capture and send, check status — carried as a visible stepper across the transfer pages
  • Receiving-account validation against the provider, returning the account holder's name before any details are captured
  • Live rate quoting, with the returned token held and checked for expiry before a transfer is composed
  • Transaction rows written before the transfer is attempted and reconciled with the provider's response afterwards
  • Saved senders offered back as pickable records, with the beneficiary's verified details carried forward from validation rather than retyped
  • Single-transaction status lookup and date-ranged history as two separate paths
  • Provider response codes and transfer statuses translated into plain outcomes at the integration boundary
  • Signed, timestamped requests over a fixed authenticated egress path, with each page carrying its own session check in front of every working screen

Outcomes

  • Led delivery of an operator console that carries a transfer end to end — validation, rate, capture, send, and the two ways of asking what happened to it afterwards
  • Set an ordering discipline that suits the domain: the record is created before the money is asked to move, so an interruption leaves an incomplete row rather than an invisible transfer
  • Made a perishable quote safe to work with by holding the provider's token with its expiry and refusing a stale transfer locally instead of discovering it at the provider
  • Absorbed the provider's transport contract at one boundary — signing, per-operation header casing and the split response codes — so the screens above it work against one settled internal shape rather than the provider's raw replies
  • Attacked the likeliest sources of operator error by design: the receiving account is confirmed with the provider before any details are entered, its verified details are carried forward as locked fields, and recurring senders are picked rather than re-keyed
  • Led the engagement as architect across a run from March 2024 to May 2025, with a two-developer team implementing against the design

More work