unirail.dev the GDS for payments

One API for every payment rail.

Unirail sits between apps that move money and the providers that can move it. Ask for a route, get back the cheapest, fastest and recommended paths, and bind your user's approval to the exact one they pick.

$ pnpm add @unirail/sdk
# server-side only: ur_test_sk_… or ur_live_sk_…
$ export UNIRAIL_SECRET_KEY=ur_test_sk_…
integrated
03
listed
10
countries
23
custody
none · a2a
accounts
payto://

01 what it does

A routing layer, not another provider.

Unirail doesn't hold your money or your keys. It knows which providers can move a payment between two accounts, what each path costs and how long it takes, and it hands your app one shape for all of them.

01

Route quotes

Ask for a route between two accounts and get up to three quotes back, each with its legs, total cost, ETA and custody spelled out.

02

Accounts are addresses

Every account is a payto:// address. Unirail returns it masked with a keyed fingerprint, never in full.

03

Approval bound to the route

Your user's passkey signs a quote's id and digest. If anything moves afterwards, the intent fails with quote_changed.

04

Keys stay in your vault

Provider credentials live in your own Infisical. Unirail reads them per call through OIDC federation and never stores them.

05

One event stream

Every provider's statuses arrive as one set of events, delivered and signed per Standard Webhooks.

06

Test and live, side by side

Environments, API keys and routing policy live in the dashboard. Every row is scoped to an environment, and so is every key.

ur_test_sk_••••••••••••••••••••••••
07

A marketplace of rails

3 providers integrated, 10 listed, and the same shape for what comes next: identity, account verification, compliance.

02 route quotes

Every payment gets a quote before it moves.

Ask for a route between two accounts. Unirail returns up to three quotes, each with its legs, total cost, ETA, success likelihood and who holds the money after every leg. Routes your policy forbids come back as infeasible, with the reason, so nothing is silently dropped.

  • custody todaynone: account to account
  • meta-providersonly where policy allows
  • approvalbound to id + digest
unirail.routeQuotes.createsample response · illustrative values
from•••• 0008
yapilycustodyAfter: none
to•••• 4410
totalCost.payer
£0.10
eta p50 / p90
6 s / 41 s
successLikelihood
0.95
firmness
firm
userActions
redirect
why
Lowest total cost to the payer.
id
qt_4KfP9x2mWqLr
digest
sha256:3a9c71d0…e10b
infeasibleairwallex holds funds in transit; your policy allows custody: none

Demo: binds the selected quote's id and digest.

03 credentials

Your credentials never leave your vault.

You keep your contracts with Plaid, Yapily or Enable Banking, and their keys stay in your own Infisical. Unirail is trusted through OIDC federation and reads a key only for the call that needs it.

your infisicalvaultowned by you
unirailper callpersistednothing
providerpisp · aispyour contract
Unirail presents an OIDC token to your Infisical, receives the provider credential for one call, calls the provider, and stores nothing.
  1. 1

    Unirail signs a short-lived OIDC token as its own issuer, scoped to your environment.

  2. 2

    Your Infisical trusts that issuer and returns the provider credential for this one call.

  3. 3

    Unirail calls the provider and lets the credential go. It never reaches a database, a log, a trace or an error.

04 marketplace

Capabilities, not vendors.

Banking comes first, then everything next to it: identity, account verification, compliance. Each capability is a rail with several providers on it, per jurisdiction, so a route can switch providers without your code noticing.

jurisdiction
PaymentsPIS, pay-in, payout
  • Plaid, integrated
  • Yapily, integrated
  • Enable Banking, integrated
  • TrueLayer, listed
  • Neonomics, listed
  • Airwallex, holds funds in transit, listed
  • Nium, holds funds in transit, listed
  • Wise Platform, holds funds in transit, listed
  • Bridge, holds funds in transit, listed
  • PayNow QR, listed
  • OpenPayd, holds funds in transit, listed
  • Modern Treasury, holds funds in transit, listed
Bank linkAIS & account linking
  • Plaid, integrated
  • Yapily, integrated
  • Enable Banking, integrated
  • TrueLayer, listed
  • Neonomics, listed
  • Wise Platform, holds funds in transit, listed
  • Moneytree, listed
IdentityKYC / identity verification
  • Plaid, integrated
Account verificationUK CoP · EU VoP · ownership
  • Neonomics, listed
  • Airwallex, holds funds in transit, listed
  • Nium, holds funds in transit, listed
  • Moneytree, listed
  • OpenPayd, holds funds in transit, listed
  • Modern Treasury, holds funds in transit, listed
FXconversion inside a route
  • Airwallex, holds funds in transit, listed
  • Nium, holds funds in transit, listed
  • Wise Platform, holds funds in transit, listed
Compliance screeningsanctions & watchlists
  • open slot · providers listing next
integratedlistedplaceholderholds funds in transitBrowse providers

05 sdk

Two calls to move money. One to hear back.

Quote, then pay with the quote your user approved. Every write takes an idempotency key you derive from your own record, and every status change arrives on one signed event stream.

  • ur_test_sk_… and ur_live_sk_… keys pick the environment
  • Webhook secrets rotate: pass both while they overlap
  • WebCrypto only: Workers, Node, Bun and Deno
import { createUnirail } from "@unirail/sdk";

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

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

// Your user approved quotes[0] with a passkey bound to its id + digest.
const intent = await unirail.paymentIntents.create(
  { quote: quotes[0].id, digest: quotes[0].digest, returnUri },
  { context: { idempotencyKey: `pay:${id}` } },
);

06 control plane

Policy lives in the dashboard, not in your code.

Decide which providers a route may use, whether money may sit with a meta-provider in transit, and which environment a key can touch, then change it without a deploy.

placeholder

Control-plane screenshot goes here

Swap in a capture of app.unirail.dev at 1600 × 1000 once the dashboard design settles. The sidebar lists what it will show.

Placeholder for a screenshot of the Unirail dashboard.

07 get started

Route your first payment in test mode.

  1. 01Create a test environment and a ur_test_sk_ key
  2. 02Point Unirail at your Infisical through OIDC
  3. 03Connect Plaid, Yapily or Enable Banking
  4. 04Quote, approve, pay, and listen for events