Payment routing by cost, speed and success rate | Unirail

Unirail

7 min read Original article ↗

Choose the provider for every payment by cost, speed and success rate

For each payment, bank link or identity check, Unirail tells your backend which of your providers to use, what it will cost under your contracts and why. It routes to Plaid, Yapily and Enable Banking today. Your backend can make the call itself with your own keys. Unirail never holds or moves money.

How one payment is routed in Decide mode

In Decide mode, the default for regulated platforms, Unirail makes the decision and your backend makes every provider call. Scroll to follow a £50.00 payment between two UK accounts from the first request to the outcome your backend reports back.

Skip the walkthrough

  1. Your backend requests a route decision

    Before it moves money, links a bank or checks an identity, your backend sends Unirail a route decision request. This one is a £50.00 payment from a UK sort code and account number to a UK IBAN, asking for the recommended rail.

    POST /v1/route_decisionsExample

    operation
    banking.pis.single

    amount
    "5000" iso4217:GBP

    payer
    GB · scan · fp_9xQ2…

    payee
    GB · iban

    preference
    recommended

    YOUR BACKEND PAYER'S BANK PAYEE'S BANK UNIRAIL · DECIDES ONLY MASKED REQUEST
  2. What the request contains

    Names, account numbers and contact details stay on your platform. Each account goes as a fingerprint you compute with your own HMAC key. Unirail can recognise the same account next time but can't reverse the fingerprint. It rejects any request with a field it doesn't expect, such as a name.

    How Decide mode keeps names and account numbers out

    Sent to Unirail

    • Operation
    • Countries and currency
    • Amount or amount band
    • Bank ids
    • Payto scheme names
    • Account fingerprints

    Stays with you

    • Names
    • Account numbers
    • Emails and addresses
    • Provider credentials
    • Your fingerprint key
    OPERATION GB · GBP 5000 SCAN · IBAN FP_… NAMES ✕ ACCOUNT NUMBERS ✕ CREDENTIALS ✕ SENT TO UNIRAIL
  3. What the decision contains

    Unirail answers with the provider and rail to use, what it will cost under your own contracts, the ETA and how likely it is to succeed. You also get the ranked alternatives, the options it ruled out with a reason for each, the policy version and a digest you can bind your user's approval to.

    How Unirail ranks providers for each payment

    Decision · routedExample values

    Use
    Provider A · Pay by bank

    Cost
    £0.15 under your contract

    ETA
    9 s at p50

    Success
    99% likely

    Alternative
    Provider B · £0.22 · 7 s · 98%

    Ruled out
    Provider C · no coverage for this payment in GB

    Policy
    v12 · digest 3f9c…e21a

    DECISION · RAIL, COST, ETA, REASONS RECOMMENDED ALTERNATIVE RULED OUT · NO COVERAGE
  4. Your backend calls the provider

    @unirail/executor runs Unirail's provider adapters inside your own process. Your backend uses it to call Provider A with credentials from your own vault. In Decide mode those credentials never leave your platform.

    With Execute via Unirail, Unirail makes this call for you using the credential from your vault.

    How your credentials stay in your vault

    Who calls whom

    Your vault → your backend
    provider credentials

    Your backend → Provider A
    the payment call

    Unirail
    not on this call

    YOUR VAULT YOUR BACKEND PROVIDER A YOUR KEYS STAY HERE PROVIDER CALL UNIRAIL · NOT ON THIS CALL
  5. The payment moves between the banks

    Provider A starts the payment under your contract. The £50.00 goes from the payer's bank straight to the payee's bank without passing through Unirail.

    £50.00 · payer's bank to payee's bank

    Initiated by
    Provider A, under your contract

    Route
    account to account

    Through Unirail
    no funds

    UNIRAIL · OFF THE PATH PAYER'S BANK → PAYEE'S BANK £50.00 · ACCOUNT TO ACCOUNT
  6. Your backend reports the outcome

    Your backend sends the rail it used, the status, the latency and the provider's fee to POST /v1/outcomes, with no names or account numbers. Unirail uses outcomes to track reliability for each rail, operation and bank. It also counts them against your contract for the billing period. The next decision is then priced at your current volume tier.

    See how the full flow works

    outcomes.createExample

    await unirail.outcomes.create({
      decision: decision.id,
      rail: decision.rail,
      status: "succeeded",
      latencyMs: 1840,
    });

    Updates
    reliability per rail, operation and bank

    Counts toward
    contract usage this billing period

    YOUR BACKEND PAYEE'S BANK RELIABILITY · CONTRACT USAGE OUTCOME · STATUS, LATENCY, FEE

What you get with Unirail

You keep your contracts with PISPs, aggregators and KYC vendors. Unirail adds these four things on top.

  • One API and SDK for all your providers

    You set up Plaid, Yapily and Enable Banking in the dashboard. Your code calls the same SDK whichever provider handles the payment.

    Start with the quickstart
  • Works with your Infisical vault

    Provider credentials stay in your Infisical vault. Adding a provider doesn't add environment variables to your services.

    Read the security model
  • Deal tracking for your provider contracts

    Unirail keeps versioned fees, volume tiers and renewal dates for each provider contract and prices every decision with them. Before you renegotiate, you can see what each provider costs you and what switching would save.

    See deal tracking
  • A routing decision for every operation

    Each decision lists ranked alternatives and gives a reason for every option it ruled out.

    How routing decisions work

Get a routing decision with one API call

Add the SDK to your backend, create a client with your test key and send a route decision request. Field names match the published v1 contract (RouteDecisionRequest, OutcomeRequest). Amounts are strings in minor units ("5000" is £50.00).

pnpm add @unirail/sdk Placeholder

The SDK isn't on npm yet. We give you access to the package when we set up your account.

  • POST /v1/route_decisions
  • POST /v1/outcomes
  • GET /v1/routing_snapshot

All 23 v1 endpoints Read the docs

decide.ts
import { createUnirail, maskAccount } from "@unirail/sdk";

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

const decision = await unirail.routeDecisions.create({
  operation: "banking.pis.single",
  amount: { value: "5000", asset: "iso4217:GBP" },
  payer: {
    country: "GB",
    scheme: "scan",
    accountRef: await maskAccount("payto://scan/040004/12345678", process.env.UNIRAIL_FINGERPRINT_KEY),
  },
  payee: { country: "GB", scheme: "scan" },
  preference: "recommended",
});

if (decision.status === "routed") {
  // Your backend calls decision.rail with your own credentials (@unirail/executor),
  // then reports what happened so the next decision learns from it.
  const started = Date.now();
  const result = await callProvider(decision); // your code
  await unirail.outcomes.create({
    decision: decision.id,
    rail: decision.rail,
    status: result.ok ? "succeeded" : "failed",
    latencyMs: Date.now() - started,
  });
}

How Unirail handles your data and credentials

In Decide mode Unirail sees masked metadata only. With Execute via Unirail it reads each credential from your vault when it needs it and never writes one to disk or a database.

YOUR PLATFORM · DECIDE MODE YOUR VAULT credentials names account numbers fingerprint key UNIRAIL masked metadata, masked outcomes decision
  • Short-lived access to your vault

    Unirail gets read-only access to one folder in your Infisical vault and keeps each credential in memory for up to five minutes. Revoking access stops new reads straight away.

  • API keys are stored as hashes

    Unirail stores only a hash and the prefix of each test and live key.

  • Encrypted configuration

    Your stored configuration is encrypted with a separate data key for each environment (AES-GCM envelope encryption).

  • Signed webhooks

    Every webhook is signed under the Standard Webhooks scheme. The SDK checks the signature for you.

  • EU data residency

    The control plane's database runs in Cloudflare's EU jurisdiction.

Read the security details Ask about self-hosting the decision engine

Start with a test key

Accounts are open by request while Unirail is in early access. We'll set yours up with a test key (ur_test_sk_…) and access to the SDK so you can send decisions from your backend. We haven't published pricing yet. Talk to us about it before you go live.

How pricing works today