Payments Engine
Self-custodial. Your keys, your infrastructure.

Accept and pay out stablecoins, without handing anyone your keys.

Two drop-in widgets and one API. Deposits are credited when the chain says so, payouts are sent exactly once, and every movement is yours to audit.

USDT and USDC firstSigned webhooksRuns on your own servers
How it works

Three calls take money in and pay it out

01

Ask for an address

One per customer account, derived from your own seed. The engine issues it and watches it; you never supply one.

POST /v1/deposit-addresses
02

Get told when it is paid

The engine reads blocks, waits for depth, and sends a signed webhook. Credited once, even across a restart or a reorganisation.

deposit.confirmed
03

Pay out under a key

An idempotency key makes a retry the same payout, never a second payment. It is signed within your limits and followed to finality.

POST /v1/payouts
Full quickstart API reference
Drop-in widgets

One widget takes money in, another pays it out

Both are iframes on your engine's own origin, so your CSS cannot restyle an address and no third party can serve a different one. Your server opens a session with its credential; the page gets a token that can read that one payment and nothing else.

your page
<div id="cashier"></div>
<script src="https://engine.example/embed.js"></script>
<script>
  // Money in. The token came from your server.
  PaymentsEngine.deposit('#cashier', { token: checkout.token })
    .on('status', (state) => {
      if (state.status === 'CREDITED') closeModal();
    });

  // Money out. Your server decided it; the customer confirms it.
  PaymentsEngine.withdraw('#cashier', { token: intent.token });
</script>
Embed guide See both inside an application

Asset and network are one choice

The same ticker on two networks is two different instruments. The widget never lets a customer pick one without the other.

The browser never decides an amount

A withdrawal is mounted with a token your server got after it checked the account. The widget can confirm it and watch it leave; it cannot change it.

It follows the payment by itself

Found, confirming, credited — read from the engine, not inferred from a timer. The parent page is told each change.

Guarantees

Each one is held by a constraint, not a convention

PropertyWhat actually holds itWhat it prevents
A payout pays onceThe signed transaction is stored with its nonce before it is sentA crash, a retry or a second worker can only send the same bytes again; the network accepts them once.
A retry never pays twiceUNIQUE (operator, idempotency_key)A timeout, a duplicated queue message, or a request that crossed a deploy all resolve to one payout.
A deposit credits exactly onceUNIQUE (chain, tx_hash, output_index)A watcher re-reading a block after a restart credits nothing a second time.
Limits survive a crashSliding 24-hour window under an advisory lockA limit cannot be reset by killing the process, and is one limit across every instance.
Two accounts never share an addressUNIQUE derivation index per key setA deposit cannot be attributed to whichever account was looked up first.
Swept funds go one placeThe signer takes no destination for a sweepMoney at a deposit address can only be moved to your own payout wallet.
Honesty

What is not built yet

Written here rather than discovered in production.

CapabilityStateConsequence for you
Bitcoin, Tron and other non-EVM networksNot builtDeposits, sweeps and payouts run on account-model (EVM) networks only.
Operator self-onboardingInterimOperators are registered and their credentials rotated through an internal admin command, without a deploy. There is no sign-up form an operator fills in themselves.
Public network run and third-party auditPendingThe whole path is exercised against a local chain on every change. It has not been run on a public network, and no external firm has reviewed custody or signing.
Questions

What an operator asks first

If I hold the keys, what happens when my server is compromised?

The design assumes it can be. Seeds are sealed under a master key you keep apart from the database, and the signer cannot be asked to sign arbitrary bytes: it builds each transaction itself, checks per-payout and 24-hour limits first, and will only sweep a deposit address to your own payout wallet. That bounds the damage; it does not remove it, and the signer belongs in an HSM before this holds real value.

Do I need to run my own chain nodes?

You need one RPC endpoint per network. Each asset in the catalogue is checked against the endpoint before it is used: the network ID must match and a token must answer with the configured precision, or it is refused.

Is the demo real?

It says which it is. With no engine behind it the demo is a simulation: signed requests are shown, but no chain is involved. Started with the local-chain command it drives a real engine on a test chain on your machine, and the blocks, transactions and webhooks are real.

Is this a money transmitter, and do I need a licence?

The software is not a service and takes no custody: it runs on your infrastructure and signs with your keys. Whether your business needs a licence depends on your jurisdiction and what you do with it.

Run it before you trust it

One command brings up a chain, a test token, the engine and this site, and walks a deposit and a payout end to end on your own machine.

Read the quickstart Open the demo