Trexure.
Features How it works Docs Contact
Talk to us Open app →
Documentation

How Trexure works

Trexure shields a Stellar payment on-chain, reconciles it against the off-chain fiat leg, and emits one accounting-ready receipt. This page covers the API you'd integrate against, the architecture behind it, and — plainly — which parts are real cryptography and which are still simulated.

Stellar testnet · What's real vs. simulated →
Contents
1. Quickstart 2. API reference 3. Webhooks 4. Receipt schema 5. Architecture 6. The ZK layer 7. Payment rails 8. Running it yourself 9. What's real vs. simulated

01Quickstart

Three calls take you from nothing to a reconciled receipt. Everything below runs against the live app on Stellar testnet — no real funds move.

1. Get an API key. Keys are tenant-scoped. Mint one from the app's API keys settings; it's shown once. Treat it like a password — send it as Authorization: Bearer <key>, server-to-server only, never in a browser bundle.

2. Create a payment.

curl -sS -X POST https://app.trexure.xyz/api/payments \
  -H "Authorization: Bearer $TREXURE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "recipientRef": "Acme Inc — invoice #42",
    "amount": "1500.00",
    "sourceAsset": "USDC",
    "targetCurrency": "PHP",
    "anchorId": "anchor_1"
  }'

Returns 201 with { "id": "…", "status": "PENDING" }. The Idempotency-Key header dedupes retries.

3. Fetch the receipt.

curl -sS https://app.trexure.xyz/api/payments/<PAYMENT_ID>/receipt \
  -H "Authorization: Bearer $TREXURE_API_KEY"

A 200 returns the same receipt document the app renders — corridor, amounts, FX, fees, on-chain and fiat legs, and privacy flags.

02API reference

Every endpoint is tenant-scoped: a key only ever sees its own tenant's payments. Programmatic calls are rate-limited per tenant, and a 429 carries a Retry-After header you should honor.

  • POST /api/payments — create a payment. Accepts recipientRef, amount, sourceAsset, targetCurrency, anchorId. Optional Idempotency-Key.
  • GET /api/payments — list payments. Supports status, corridor, cursor, limit.
  • GET /api/payments/{id}/receipt — fetch the reconciled receipt for one payment.
  • POST /api/webhooks/endpoints — register a settlement webhook. GET to list, DELETE /{id} to remove.

Response codes on the receipt endpoint:

  • 200 — receipt returned.
  • 401 — missing or invalid API key.
  • 404 — no receipt for that payment, or it belongs to another tenant. The two are deliberately indistinguishable.
  • 429 — rate limit exceeded; back off per Retry-After.

Machine-readable spec. An OpenAPI 3 document covering these endpoints ships with the codebase (docs/api/openapi.yaml). A hosted, browsable version of it — and an npm-published SDK — are still to come; today the TypeScript client lives in the repository rather than on the registry.

03Webhooks

Rather than polling, register an HTTPS endpoint and Trexure will POST a signed payment.settled event when a payment settles. The secret is returned once at registration.

{
  "id": "evt_pay_123",
  "type": "payment.settled",
  "created": "2026-07-20T00:00:00.000Z",
  "data": {
    "paymentId": "pay_123",
    "status": "SETTLED",
    "receiptUrl": "https://app.trexure.xyz/api/payments/pay_123/receipt"
  }
}

Every delivery carries an X-Trexure-Signature header — a hex HMAC-SHA256 of the raw request body under your endpoint secret. Verify it with a constant-time compare before trusting the event:

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody: string, signature: string, secret: string): boolean {
  const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(signature);
  return a.length === b.length && timingSafeEqual(a, b);
}

Delivery is at-least-once with retry and backoff, so dedupe on id. A dead partner endpoint never affects reconciliation — delivery failures are isolated from the settlement path by design.

04Receipt schema

The receipt is the point of the product: one document an accountant can read and an auditor can verify, without either of them needing a block explorer.

{
  "id": "rcpt_demo_fiat_settled_1",
  "paymentId": "pay_123",
  "status": "settled",
  "corridor": { "from": "USD", "to": "PHP" },
  "amounts": {
    "source":      { "currency": "USDC", "value": "1500.00" },
    "destination": { "currency": "PHP",  "value": "84000.00" }
  },
  "onchain": { "txHash": "abc…", "ledger": 3500123, "asset": "USDC" },
  "fiat":    { "provider": "mock-anchor", "bankRef": "BDO-DEMO-1500" },
  "privacy": { "shielded": true, "viewKeyDisclosed": false }
}
  • onchain — the Stellar leg: real transaction hash and ledger sequence, verifiable on any explorer.
  • fiat — the off-chain leg, named by provider so you can always tell a simulated payout from a real one.
  • privacy — whether the payment was shielded, and whether a view key has been disclosed to an auditor.

05Architecture

Two services over shared data, with the chain on one side and the banking rail on the other. Nothing exotic: the interesting part is that the reconciliation engine never needs to see plaintext payment details.

Trexure architecture: a Next.js web service and a BullMQ worker over PostgreSQL, Redis and S3-compatible storage, talking to Soroban RPC and Horizon on one side and an anchor on the other.

  • Web service (Next.js 16, App Router) — RSC UI, route handlers under /api/**, and a middleware layer enforcing the session gate plus a strict nonce-based CSP and HSTS.
  • Worker service (BullMQ) — a watch-onchain job that confirms commitments against Soroban events, and a reconcile job that matches the on-chain and fiat legs.
  • Data — PostgreSQL 17 via Prisma 7 (with a query extension that forces a tenant scope on every tenant-owned read and write), Redis for queues, rate limits and idempotency, and S3-compatible object storage for receipt PDFs.
  • Chain — Soroban RPC and Horizon on Stellar testnet.

06The ZK layer

Trexure proves a payment's integrity with a Groth16 zero-knowledge proof verified on-chain, using Soroban's native BLS12-381 host functions (env.crypto().bls12_381()) rather than any application code we could fake.

The contract's verify entrypoint:

  • Recomputes the public-input commitment vk_x = IC[0] + Σ pubᵢ · IC[i+1] with a G1 multi-scalar-multiply (g1_msm) followed by g1_add. Public signals are 32-byte big-endian scalars, each below the BLS12-381 scalar-field order.
  • Runs one multi-pairing check — e(−A, B) · e(alpha, beta) · e(vk_x, gamma) · e(C, delta) == 1 — over four G1/G2 pairs. The caller pre-negates A so verification collapses into a single pairing_check, the cheapest way to spend Soroban's pairing budget.
  • Returns true only if the product of pairings is the identity. Tamper with any public signal and vk_x changes, so the check fails.

A second entrypoint, shielded_transfer, records a payment's commitment on-chain as a contract event (topics = (intentId,), data = commitment) that the watch-onchain worker confirms against.

Honest scope. The circuit is deliberately minimal — knowledge-of-opening of a commitment. What's real is the cryptographic machinery: a genuine Groth16 proof, a genuine on-chain pairing verification, and a tampered statement that gets rejected. shielded_transfer is a commitment recorder: it anchors the intent and commitment on-chain but does not itself move tokens. A richer privacy-pool circuit swaps in behind the same prove/verify interface.

07Payment rails

Two rails, with genuinely different privacy and reconciliation models:

  • Rail A — private on-chain transfer. Real XLM moves through a Soroban ShieldedPool to any recipient wallet, with the sender↔recipient link hidden in zero knowledge. There is no fiat leg to match: the withdrawal transaction is the settlement.
  • Rail B — private fiat payout. Value leaves the pool into custody and an anchor pays a bank account. The on-chain and fiat legs then reconcile by a shared intent ID into a settled payment and a receipt.

Recipients don't need an account. A payout email carries a signed, single-payment claim link; opening it is enough to cash out to a Stellar wallet (Freighter, LOBSTR, xBull and other SEP-43 wallets) or to a Philippine bank account.

Enabled per environment. Both rails above — the shielded pool and the link-only recipient claim — are real, tested code, but they ship behind a feature flag that is off by default. They may not be exercisable on a given deployment. Ask us for a walkthrough on an environment where the rail is switched on.

08Running it yourself

Trexure runs locally on a standard stack: Node 22 with pnpm, PostgreSQL 17, Redis 7 and MinIO via Docker Compose, then Prisma migrations, a seed, and the web and worker processes. The test suite runs offline against the local database; the ZK tooling (circom, snarkjs, the Stellar CLI and a Rust wasm32v1-none target) is only needed if you want to rebuild the verifier.

The source isn't public yet. The repository is currently private, so there is no public clone URL or published license to link here. If you're evaluating Trexure and want access to the code or the full setup guide, get in touch and we'll arrange it.

09What's real vs. simulated

Trexure is a working system with clearly-labelled seams, and we'd rather tell you where they are than have you find them. The rule we build to is: mock the provider, never the verification.

  • Real. The Groth16 proof and its on-chain BLS12-381 pairing check. The Stellar transactions, hashes and ledger sequences on the on-chain leg. The shielded pool withdrawal and its nullifier-based double-spend protection. The reconciliation engine, the receipt, and every signature on every webhook.
  • Simulated, on purpose. The fiat off-ramp is a mock anchor that fires a genuinely signed webhook at the real reconciliation endpoint — so everything downstream of the provider is production code. FX rates come from a fixed demo table rather than a live quote API. The treasury-yield swap leg is simulated rather than routed through a real DEX.
  • Testnet only. No real funds move anywhere in the system today.

Each of those seams sits behind an interface built to be swapped — a real anchor over SEP-24/31, a real DEX path payment — without changing the engine around it.

Trexure.

A unified, privacy-preserving treasury API for Stellar. Shielded on-chain, reconciled with fiat, delivered as a clean receipt.

Product

Open the app Features How it works

Developers

Quickstart API reference Webhooks Architecture The ZK layer What's real vs. simulated

Legal

Privacy Policy Terms of Use
© 2026 Trexure. A product of Artisam Labs. app.trexure.xyz· hello@artisam.xyz· Privacy· Terms