Reconciliation infrastructure · retail & payments

Find where your systems disagree

Your POS, your processors, your order ledger and your bank each tell a different story about the same money. Okwan matches them record by record and says what agrees, what differs and why, as an engine your product embeds.

  • Read-only by construction
  • Credentials sealed per tenant
  • Open core, Apache-2.0

recon.across_rails

Shopify orders against Stripe and PayPal

Example · test data
  • #1002

    $999.00

    Charged twice

    Both rails took the full total. $999.00 is owed back.

  • #1003

    $150.00

    Paid once

    Matched on the order reference.

  • #1004

    $450.00

    Charged twice

    The same order, collected on both rails.

  • #1005

    $120.00

    Fee explained

    PayPal's $4.68 fee accounts for the gap exactly.

  • #1006

    $75.00

    No payment

    Marked paid in the ledger. Nothing on any rail.

  • —

    no order

    No order

    Collected against #9999, which doesn't exist.

2 orders charged twice · $1,449.00 owed back

read: Shopify · Stripe · PayPal

An example modelled on a test-mode run across a Shopify store, Stripe and PayPal, in which the engine found orders #1002 and #1004 charged on both. Each rail on its own reported a clean match; only the check across them shows the double charge.

6

connectors live today

7

verdicts, where most tools give two

1 → 3

one definition becomes an API, a SQL view and an agent tool

0

write access to any payment rail

Retail first

Built for the retail stack.

For POS dealers, ISVs and payment providers whose merchants sell in store and online, and collect on more than one system. Three reconciliations run today; the next three are chosen with the platforms we work with.

Live

Orders against every payment rail

Each order traced to the rail that collected it, across Stripe and PayPal at once: paid once, charged twice, split across rails, or not paid at all.

GET /v1/reconciliations/across/rails

Live

Fees and refunds, explained

When a processor fee or a refund accounts for a gap exactly, it is named as the cause, so the list you chase holds only real breaks.

explains=[rail_fee, refund]

Live

Your own database, against any rail

Point a declaration at a query over your own Postgres and reconcile it against a processor like any other system.

ResourceRef(connector="postgres", resource="sql")

In discovery

POS batches against processor deposits

Card batches closed at the register against what the processor actually deposited, by day and by location.

In discovery

Gift cards, tips and chargebacks

The adjustments that make a day's sales and a day's deposits disagree, each named rather than netted away.

In discovery

Residuals against the merchant book

For dealers and ISOs: processor residual statements checked against the merchants and volume you expect to be paid on.

Which of the three in discovery costs your merchants the most? Tell us; it decides what we build next.

Beyond retail

One engine. Any two systems that should agree.

Retail is where we start, not where the engine stops. A reconciliation is a declaration over any two systems that can list their records, so a new industry is a set of connectors, not a new product.

Healthcare

Claims ↔ remittances

What was billed against what payers actually paid, and why the rest did not arrive.

Logistics

Carrier invoices ↔ shipments

Every invoiced shipment against one that moved, at the rate that was agreed.

Subscriptions

Billing ↔ payouts

Invoices and renewals against the money that settled, after fees, refunds and disputes.

Marketplaces

Orders ↔ seller payouts

What buyers paid against what each seller received, net of commission.

These use the same engine as retail; each needs its systems' connectors first.

The distinctions are the product

Seven verdicts, not two.

A tool that only says matched or unmatched turns every refund into a false alarm and pairs look-alike payments by coin flip. Okwan tells them apart, and says what it could not see.

Agrees

Matched, and the amounts agree.

Differs, explained

Matched; a fee or a refund accounts for the gap exactly.

Differs, unexplained

Matched, and nothing accounts for the gap. The number to chase.

Ambiguous

More than one equally good candidate. Reported, never guessed.

No payment

In the ledger, and on no rail that was read.

No order

Collected, with no record it belongs to.

Unverifiable

The other side was not read far enough back to say. Never called missing.

How it works

One definition. Three interfaces.

Declare the match once. Okwan generates a REST route, a SQL view and an agent tool from that one declaration, with nothing hand-written in between, so the three can never drift apart.

Reconciliation(
    name="shopify_stripe",
    left=ResourceRef(connector="shopify", resource="orders",
                     operation="list"),
    right=ResourceRef(connector="stripe", resource="charges",
                      operation="list", params={"status": "succeeded"}),
    keys=[
        ExactRef(left="name", right="order_ref"),
        Fuzzy(amount="total_received_minor", currency="currency",
              amount_right="amount", timestamp_right="created",
              window="7d"),
    ],
    amount=AmountRef(left="net_payment_minor", right="net_minor"),
    explains=[Explains(path="fee_minor", side="right", label="rail_fee")],
)
REST

An endpoint

GET /v1/reconciliations/shopify_stripe, with request validation and an OpenAPI reference.

SQL

A queryable view

recon.shopify_stripe, joinable with anything else in the catalog and fetched live at query time.

MCP

A tool for your agents

okwan_reconcile on the hosted MCP server. Read-only by construction.

For developers

Query across systems in one statement.

Federated SQL over live connectors. Only the tables a query names are fetched, and only when it runs: no pipeline, no copy of your merchants' data.

Connected today

  • Shopify
  • Stripe
  • PayPal
  • Paystack
  • PostgreSQL
  • WhatsApp
Read the API reference
curl https://okwan.onrender.com/v1/query \
  -H "Authorization: Bearer okw_…" \
  -d '{"sql": "SELECT o.name, o.total_price_minor, c.amount
               FROM shopify.orders o
               LEFT JOIN stripe.charges c
                 ON c.order_ref = o.name"}'

Security

Built to be trusted with merchants' money.

Okwan holds credentials to your merchants' payment systems, so the guarantees are structural: enforced by how the system is built, not by a setting someone can change.

Read-only by construction

A reconciliation over a write operation cannot be declared. Okwan reads; it never moves money.

Credentials sealed per tenant

Each stored credential has its own encryption key and is bound to its tenant, connector and field. None travels in a request.

Keys shown once

API keys are stored as hashes. A lost key is replaced, never read back.

Isolated by default

Another account's tenant answers 404, not 403, so the API cannot reveal who else is a customer.

Tell us what your merchants can't reconcile.

We are working with a small number of retail and payments platforms. Bring one reconciliation your team or your merchants do by hand, and we will show you the verdicts.