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.
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.
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.
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.
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.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.
/api/**, and a middleware layer enforcing the session gate plus a strict
nonce-based CSP and HSTS.watch-onchain job that
confirms commitments against Soroban events, and a reconcile job that
matches the on-chain and fiat legs.
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:
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.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.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.
Two rails, with genuinely different privacy and reconciliation models:
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.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.
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.
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.
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.