Unirail
Unirail CentralEvery payment rail · Platforms 1–4

One API for every payment rail.

Unirail is the signal box for platforms that move money: one API over the PISPs, aggregators and KYC vendors you already have deals with, and a decision on every payment, with its provider, rail, cost and reason. Your provider moves the money. Unirail never touches it.

  1. 08:43GB→GBFASTER PAYYAPILY£50.00NO CUSTODY1QUOTED
  2. 08:44FI→DESEPA INSTANTENABLE BANKING€120.00NO CUSTODY1QUOTED
  3. 08:46GB→SGFX PAYOUTAIRWALLEX£300.00IN TRANSIT1NOT ADMITTED
  4. 08:47GBIDENTITYPLAID2CHECKING
  5. 08:49GB→GBFASTER PAYPLAID£18.40NO CUSTODY1QUOTED
  6. 08:50GBCOP CHECKOPENPAYD3LISTED
  7. 08:52NL→NLSEPA INSTANTENABLE BANKING€42.00NO CUSTODY1QUOTED
  8. 08:53SG→SGPAYNOW QRSGQR · DIRECTS$25.00NO CUSTODY1LISTED

Platform 1 · Banking08:46

GB → SG FX payout via Airwallex

NOT ADMITTED Infeasible under this platform’s routing policy. The route is returned with its reason, never silently dropped.

From
payto://scan/04••04/••••5678
To
payto://sgacct/71••/••••7890
  1. Payer’s bank → Airwallex. Custody after: provider-transit.
  2. Airwallex → payee’s bank on a local rail. Custody after: none.

Airwallex holds the money between collection and payout. This platform’s policy admits no-custody routes only, so Unirail returns the route under infeasible, with that reason, instead of quoting it.

Signal boxTwo ways to run

The signal box, not the train.

Unirail sets the points and never touches the money. You keep your licences, your provider deals and your own KYC and AML. Unirail gives you one API over those providers, your secrets manager wired in, your deals next to live routing data, and a decision with its cost and its reason.

Choose a mode

Decide Unirail answers, your backend drives

  1. You send masked metadata only. No PII, no account numbers.
  2. Unirail returns the decision: provider, rail, cost and why.
  3. Your backend calls the provider through the SDK, with your own credentials.

Unirail never touches secrets or money. The decision engine can be self-hosted.

Execute via Unirail Unirail makes the provider call for you

  1. You send the route your user approved, bound to its id and digest.
  2. Unirail calls the provider for you, reading the credential just in time from your vault.
  3. Your provider moves the money; the events come back on one stream.

The secret is read for that call only and never stored. The money still never passes through Unirail.

How it worksCalling at

Four stops from a quote to money that’s arrived.

One request, one choice, one approval, one event stream. The stops are the same whether the train runs on Faster Payments out of London or SEPA Instant out of Helsinki, and whichever of your providers pulls it.

  1. Quote

    Ask for every route between two payto:// addresses. Up to three come back, labelled cheapest, fastest and recommended, each with its legs, total cost, ETA and custody. The ones that can’t run come back too, with the reason.

    routeQuotes.create()
  2. Choose

    Your user picks. Everything they need to decide is on the quote, so nothing gets chosen behind their back. Your routing policy only sets the default.

    quotes[i]
  3. Approve

    Bind their approval, a passkey for example, to the quote’s id and digest. If anything they approved has changed, the intent is refused with quote_changed.

    { id, digest }
  4. Depart

    Create the payment intent with an idempotency key. Your provider moves the money; you follow it on one event stream: requires action, processing, succeeded.

    paymentIntents.create()

TimetableRoute quotes

Every route, with its fare, its time and who holds the money.

Custody is data on every leg, not a footnote. Today Unirail routes account to account with nobody in the middle; meta-providers such as Airwallex hold money in transit, so they only appear where your policy admits it. Anything left out is still printed, struck through, with the reason.

Your user picks · £50.00 GB → GB
LabelViaLegsETA p50 / p90FareCustody
CheapestEnable BankingFaster Payments17 s / 40 s£0.12none
FastestPlaidFaster Payments13 s / 18 s£0.30none
RecommendedYapilyFaster Payments15 s / 25 s£0.20none
Not admittedaAirwallexcollect + payout2——provider-transit

a Holds the money in transit; this platform’s policy admits no-custody routes only. Returned under infeasible with that reason.

Passkey signs quote id + digest

Specimen quote: providers, fares and times show the shape of a quote, not real prices.

Platforms 1–4Marketplace

Banking first. Then KYC, verification and compliance, from the same concourse.

Each capability is a platform with several providers per jurisdiction, and the marketplace lists them all. You keep your licences, your contracts and your own KYC and AML; Unirail gives you one API across whichever providers you connect. Integrated ones are in service, listed ones are on the timetable.

Platform 1: Banking

Bank linking, pay-ins and account-to-account payments.

Calling at

  • PlaidGBIn service
  • YapilyGBIn service
  • Enable BankingGB + 14 EEAIn service
  • TrueLayerGBListed
  • NeonomicsNordicsListed
  • AirwallexGB EEA SG HK AU USListed
  • NiumSG GBListed
  • Wise PlatformGB EEA USListed
  • OpenPaydGB EEAListed
  • Modern TreasuryUSListed
  • BridgeUSListed
  • PayNow QRSGListed

Platform 2: KYC

Identity verification through your own KYC vendors.

Calling at

  • PlaidGBIn service

Platform 3: Verification

Account checks: UK Confirmation of Payee, EU Verification of Payee, ownership.

Calling at

  • AirwallexCoP · VoPListed
  • OpenPaydCoP · VoPListed
  • NiumNium VerifyListed
  • NeonomicsNordicsListed
  • MoneytreeJPListed
  • Modern TreasuryUSListed

Platform 4: Compliance

Screening vendors per jurisdiction, behind the same API.

Calling at

3 in service10 listed23 countries

  • GB
  • IE
  • FR
  • BE
  • NL
  • DE
  • AT
  • IT
  • ES
  • PT
  • FI
  • EE
  • LV
  • LT
  • SK
  • NO
  • SE
  • DK
  • US
  • SG
  • HK
  • AU
  • JP
Browse providers

Left luggageCredentials

Your keys stay in your locker. Unirail never takes them home.

Provider credentials stay in the secrets manager you already run, Infisical first, so adding a provider means no new env vars to ship. In Decide mode Unirail never sees them at all: your backend calls the provider itself. In Execute mode Unirail reads a secret just in time, over OIDC, for the one call that needs it, and keeps no copy: not in its database, its logs, its traces or its errors.

  1. Trust once. Point your Infisical at Unirail’s OIDC issuer.
  2. Show a ticket. Each call carries a short-lived token your vault can check.
  3. Collect, use, return. In Execute mode the secret is read for that call and dropped after it.
  4. Take it back. Remove the trust in Infisical and Unirail can read nothing.

Locker 106 is your vault. Unirail only ever holds the claim ticket.

Ticket officeControl plane

Run the whole station from one window.

The dashboard at app.unirail.dev is where you connect the providers you contract with, track those deals, write routing policy and read every event. Test and live never mix.

Window 1 Open

Environments & keys

Test and live are separate stations: keys, connections, policy and events each belong to one. Your server holds the secret key as UNIRAIL_SECRET_KEY.

Window 2 Open

Deals & fares

Your contracts, fees and volume tiers sit next to live routing data, so you negotiate knowing what switching provider would save.

Window 3 Open

Routing policy

Which providers serve which routes, which custody you admit, and the default between cheapest, fastest and recommended. Every change records a reason.

Window 4 Open

Webhooks & events

One event stream across every provider, signed and delivered as Standard Webhooks, with the log in the dashboard.

[ screenshot: app.unirail.dev → Routing policy ]Dashboard capture goes here once the policy screen is final.
Open the dashboard

InformationDevelopers

Your ticket: one SDK, two calls, every rail.

Unirail · Single journeyQuote → intent

import { createUnirail } from "@unirail/sdk";

const unirail = createUnirail({ apiKey: process.env.UNIRAIL_SECRET_KEY });

const { quotes } = await unirail.routeQuotes.create({
  from: payer,
  to: payee,
  amount: { value: "5000", asset: "iso4217:GBP" },
});

const intent = await unirail.paymentIntents.create(
  { quote: quotes[0].id, digest: quotes[0].digest, returnUri },
  { context: { idempotencyKey: `pay:${id}` } },
);

Admit one

@unirail/sdk

Class
Test & live
Auth
API key
Bind
id + digest