Skip to content
Integration

Your systems keep the money. Ours keeps the arithmetic.

The platform is a computation and a record. It receives what happened in your systems, works out what is owed, and sends back instructions. It never sits in the money path.

The custody boundaryA system map split by a single heavy rule. On the operator's side: their Stripe account, their NOWPayments account, their bank, their wallet and their own backend. On the platform's side: ledger, runs, commands, claims and outbox. Four lines cross the boundary, carrying a command, a receipt, an instruction and an event. None of them carries money, and the ledger and runs cross nothing at all.OPERATORXEXCOMMANDRECEIPTINSTRUCTIONEVENTCUSTODY BOUNDARYSTRIPE ACCOUNTNOWPAYMENTS ACCOUNTBANKWALLETYOUR BACKENDLEDGERRUNSCOMMANDSCLAIMSOUTBOXNo line here carries money. XEX moves instructions; the operator moves money.LEDGER AND RUNS CROSS NOTHING
Four things cross the boundary: a receipt, a command, an instruction and an event. None of them is money.
The contract

A published spec, and two clients written against it.

An OpenAPI document in the repository, a TypeScript client and a Dart client. Your engineer can read the surface before anyone books a call.

OpenAPI

The whole public surface, in one document: member endpoints, admin endpoints, the command API and the event catalogue.

api/openapi.yaml

TypeScript client

For your web and Node services. Typed against the same document.

sdk/typescript

Dart client

For a Flutter app, because a member-facing programme usually has one and hand-rolling a second HTTP layer is where the two drift.

sdk/dart

Outbound

Events you can trust to arrive.

Eleven event types, enqueued in the same transaction as the state change that produced them.

  1. 01

    A transactional outbox, not a fire-and-forget POST

    An event is written in the transaction that caused it and dispatched afterwards from a leased queue. There is no window in which your system was told about something the ledger then rolled back, and no window in which the ledger committed and nobody was told.

    internal/outbox

  2. 02

    Signed, with a rotatable key id

    Every delivery carries an HMAC-SHA256 signature with a key id and a timestamp. Rotation overlaps: the old key stays valid while you cut over, so rotating a secret is not an outage you have to schedule.

    internal/outbox/sign.go

  3. 03

    Settlement events go first

    The claim instruction is not queued behind a backlog of notifications. Delivery classes are separate lanes, and settlement is the one that carries a money instruction.

    internal/events

  4. 04

    Deterministic ids, so a re-drive is a no-op

    Re-driving a handler re-enqueues the same event id, which the outbox dedupes. At-least-once delivery you can actually retry, rather than at-least-once delivery you have to be careful with.

    internal/events · internal/outbox

  5. 05

    An egress guard on your URL

    The destination is operator-configured, which makes it an SSRF vector by construction. The dispatcher checks the URL before the request and blocks the addresses you would expect it to block.

    internal/outbox/ssrf.go

  6. 06

    Backoff, parking and a reconciler

    Failures back off and eventually park rather than hammering a destination that is down. A cross-system reconciler catches a genuinely lost event rather than trusting the queue to be perfect.

    internal/outbox · internal/reconciler

Inbound

Commands that run exactly once, keyed on your record ids.

You tell the platform a purchase happened, a refund happened, a member was verified. Retrying is free, because the key is yours.

Every inbound command is idempotent per workspace and reference, and the response is cached. Send the same purchase twice and the second call returns the first call's answer — it does not create a second purchase, and it does not pay a second round of commissions on the first.

The error contract is explicit, because “retry or not” is the question a client integration actually has to answer. A returned error releases the receipt and you may retry. A rejection with a 4xx code is terminal and cached: the command was refused on its merits and retrying it will refuse it again.

internal/commands · internal/command

Money out

Settle however you already settle.

The platform reserves the exact ledger lines and instructs. What executes the payment is yours.

If you have a settlement host, it receives the claim instruction as a settlement-class event and calls back to settle or fail. If you have not, the payouts console lists what is owed, you pay from your own wallet, and you record the executed price and your own transaction reference. A payment recorded with no reference is refused — it could never be reconciled against your wallet, so accepting it would only mean the reconciliation failed later and further away.

internal/claims · internal/httpapi/payouts.go

Your brand

Your own domain, proved before it is provisioned.

Claim the hostname, publish the TXT record the platform generates, and provisioning happens once it resolves.

The order matters and it is the whole point. Binding a hostname — or adding a domain to the identity provider's authorised list — before proof of control would let whoever actually owns that name receive sign-ins meant for you. So nothing is provisioned, and nothing is added to the sign-in allow-list, until the record resolves. A background provisioner picks it up within the minute.

internal/domains

Next

Take a sandbox and point it at a staging system.

Free on every plan, with the same API surface, the same events and the same signatures as production.