ISO 20022 boundary layer
Keep the MT core.
Speak MX at the boundary.
eBridge sits between an MT-based application and an ISO 20022 network. It parses FIN, resolves it into a canonical payment, generates and validates MX — and records exactly what happened to every field, including the parts that could not be carried.
- Runtime
- Java 21, Spring Boot, Apache Camel routes
- Parse
- Prowide Core for FIN, generated JAXB bindings for MX
- Rule packs
- CBPR+, HVPS+, SEPA, local RTGS variants
- Deploy as
- Inline gateway, sidecar, or embedded library
- Direction
- MT → MX and MX → MT, same rule set
The design decision that matters
Never map text straight to XML
A direct MT-to-MX transformation works once, for one message pair, and then has to be rewritten for the next. eBridge resolves every inbound message into a canonical payment first. Parsing, interpretation and generation stay separate concerns, so a new message pair is a new translator — not a new codebase.
// the middle is a business object, not a string public record CanonicalPayment( String transactionReference, String uetr, BigDecimal amount, String currency, LocalDate settlementDate, Party debtor, Account debtorAccount, Party creditor, Account creditorAccount, AgentChain agents, Charges charges, RemittanceInformation remittance ) {}
public interface MessageTranslator<I, O> { O translate(I source); } // MT side Mt103ToCanonicalTranslator // MX side CanonicalToPacs008Translator
| MT | Canonical | MX | Direction |
|---|---|---|---|
| MT103 | Payment | pacs.008 | both |
| MT202 / MT202COV | Payment | pacs.009 | both |
| MT940 | Statement | camt.053 | both |
| MT942 | Statement | camt.052 | both |
| MT900 / MT910 | Advice | camt.054 | both |
| MT199 / MT299 | Status | pacs.002 | both |
| MT101 | Payment instruction | pain.001 | both |
The same canonical model also serves a JSON contract, which is how internal consumers get off FIN text without waiting for the core to change. That path is the reason the adapter can be temporary.
What happens to a message
Six stages, in this order, every time
The order is the design. Nothing is generated before it is understood, and nothing leaves before it is validated against the rulebook that actually applies to the corridor.
Parse
Blocks 1 to 5 are read separately. Tag grammar, field options, repetition and the permitted character set are checked before any interpretation happens. Field 121 is lifted early so the UETR survives whatever follows.
Interpret
Letter options are resolved: option A is a BIC, option F is structured, option K is free text and gets treated as such. Code words in fields 23E, 70 and 72 are matched against the corridor's rule set rather than guessed at.
Resolve to canonical
Parties, accounts, the agent chain, charges and remittance become typed objects. Amounts become decimals with an explicit currency. Dates become dates. Anything that cannot be placed is recorded, not discarded.
Apply business rules
Enrichment, corridor defaults, purpose-code derivation, screening hand-offs and any bilateral agreement in force. Rules are versioned and every applied rule leaves its identifier on the message.
Generate MX
The target version is selected per corridor, the business application header is built, and the document is produced from generated bindings rather than string templates.
Validate and release
Schema validation, then rulebook validation, then the fidelity check. A message with an unresolved ambiguity is held in the repair queue instead of being released in a shape nobody chose.
The honest part
Translation is not lossless. eBridge tells you where
Most tools in this space imply a clean equivalence between the two formats. There isn't one. MX can hold more structure than MT can express, and MT can hold text that MX has nowhere to put. eBridge stamps every field it touches with an outcome and keeps the set for the life of the message.
Source content carried into the target without loss or inference.
The target element was populated by a rule rather than by the source. The rule identifier is stored alongside the value, so the decision is attributable.
Content exceeded the target's length. The full source is retained in the audit store and an alert fires above a configurable rate.
Source content had no destination. Retained and reported rather than dropped; release policy decides whether the message still goes.
Interpretation is ambiguous. The message is held with the specific field flagged, not the whole payload.
Translation would produce a message that is invalid or misleading. Returned to the sending application with a reason code.
Structure has to be inferred
MX holds more than MT, so this direction looks easy. It isn't, because the MT content is often unstructured:
:50K:/1001234567
JOHN M SIBANDA
12 MAIN ROAD
HARARE ZW
Is line three a street, a building, a department? Is line four a town and country, or a town and a region? eBridge will place these in address lines, and it will offer an inference from a country dictionary — but it will not apply the inference silently. Either the corridor rule set accepts it as DEFAULTED, or the message waits for a human.
Something has to give
This direction is harder. The source can carry more than the target can express, and every option costs something:
- truncate
- concatenate
- drop
- prioritise
- reject
- store outside the message
eBridge makes the choice explicit per element and per corridor, keeps the complete MX document addressable by UETR, and reports the loss rather than absorbing it. Structured addresses that will not fit into four lines are the usual casualty, and the count is the thing to watch.
Three ways to put it in
Deployment patterns
All three run the same translation core. The difference is where the boundary sits and how much operational weight the adapter carries.
Inline gateway
The adapter sits in the live path. Fastest to adopt and it changes nothing upstream, which is exactly why it becomes operationally critical on day one. Size the repair desk before you cut over, not after.
Dual-processing bridge
Both paths run, outputs are diffed, and nothing is released on the strength of the new one until the difference report is boring. This is how you earn the confidence to switch, and it is the pattern most programmes skip and later regret.
API facade
The application stops depending on either format and talks to a canonical payment contract instead. Slower to reach and the strongest long-term position, because the adapter behind it can be replaced or removed without touching the caller.
Runtime
Boring where it counts
Payments infrastructure is not the place for novelty. The stack is deliberately conventional so that the interesting risk stays in the mapping rules, where it belongs.
| Concern | Choice | Why |
|---|---|---|
| Application | Spring Boot on Java 21 | Operational familiarity in the banks that run MT today |
| Routing | Apache Camel | Endpoint variety without bespoke plumbing per channel |
| FIN parsing | Prowide Core | Maintained MT model; licensed editions available for production |
| MX binding | Generated JAXB classes | Compile-time safety against the schema, per version |
| Rule validation | Schematron + rule engine | Rulebook constraints live outside the code that maps |
| Transport | Kafka | Replay, back-pressure and an ordered audit source |
| Store | PostgreSQL | Correlation, fidelity ledger and audit retention |
| Idempotency | Redis | Duplicate suppression across restarts and instances |
| Observability | OpenTelemetry | One trace per UETR across every hop |
Version management is a feature, not a migration
Schemas and market practice change on a published cycle. The adapter carries multiple target versions at once and selects per corridor and per counterparty:
pacs.008.001.08 // in production pacs.008.001.09 // pilot corridor pacs.008.001.10 // loaded, not enabled
Rule packs version independently of schemas, because CBPR+ constrains a schema without changing it. A corridor is a pairing of the two, and it is switchable without a release.
Run it in anger
What separates a converter from an adapter
Field conversion is the small part. These are the properties that decide whether the thing survives an audit and a bad Monday.
One trace per UETR
Every transformation step, rule applied and value changed is recorded against the end-to-end reference, from the moment the MT arrives to the moment the MX is acknowledged.
Idempotency and duplicate detection
Replays, retries and redeliveries resolve to a single translation. The trailer's duplicate flag is honoured, and so is your own reference window.
Status tracking both ways
Acknowledgements, rejections and status reports are matched back to the originating instruction and surfaced in the format the sending application already understands.
Error, reject and repair queues
Three queues, not one: a syntax failure, a rulebook failure and an ambiguous mapping are different problems with different owners and different clocks.
Logging that will not leak
Party names, accounts and remittance detail are masked in logs by classification, with unmasked content confined to the audit store and its own access path.
Conformance suite
Mapping is tested against the published sample messages for every message pair, plus a corridor-specific regression set that grows with every repair you ever had to make.
Use it as a bridge, not a building
The best adapter is one you can eventually remove
An adapter earns its place as transition architecture. It stops earning it the moment it becomes the reason every internal system is still MT-based. Plan the exit while you plan the entry:
- Phase 1Translate at the boundary. MT stays internal, MX goes to the network, dual-run proves the mapping.
- Phase 2Expose canonical APIs. Internal consumers move off FIN text onto the payment contract.
- Phase 3Go MX-native inside. Structured parties, full remittance, ISO 20022 persisted rather than derived.
- Phase 4Retire the translation on the flows that no longer need it. Keep it where a counterparty still speaks MT.