Skip to content

Receipts & Reconciliation

Availability: GET /v1/executions/{reference}/receipt is implemented and deployed. A receipt is issued when the payout reaches completed; until then the endpoint returns 409 receipt_not_issued. Partner traffic is still gated on a separate operational step, so a partner execution does not reach completed yet. GET /v1/reconciliation is still coming soon. Sandbox host rollout (api.ezys.sh) is in progress.

The body below is frozen as ezys-receipt-1. One field — payout.beneficiary — is specified but not yet returned; see Payout block.

Machine-readable proof of what an execution actually did — the FX conversion that happened (offramp block), the fiat delivery that happened (payout block), and the fees charged — plus an aggregate reconciliation view for joining many executions against your own ledger.

Payout partners each produce different receipt formats; Ezys normalizes them into the single schema below, so consumers never handle provider-specific shapes.

Get receipt

GET /v1/executions/{reference}/receipt

Returns the normalized receipt for one completed execution. {reference} resolution follows the same rules as the Status API (refTypeclientReference (default) or executionId). Track progress on the Status API until the receipt is issued.

StatusCodeWhen
404receipt_not_foundUnknown reference, or one belonging to another tenant
409receipt_not_issuedThe reference is valid but the execution has not reached completed — including one that terminally failed before completion. A partial body is never returned in its place
500receipt_corruptThe stored body failed to read back as a JSON object. Loud by design: a malformed receipt is never silently reshaped

One execution carries exactly one receipt. This version covers a single payout leg; a reference that resolves to multiple legs (split fill) is rejected explicitly rather than answered with one of them.

Read endpoints are authenticated with an API key (X-API-Key) — see Authentication.

Response

json
{
  "_schemaVersion": "ezys-receipt-1",
  "clientReference": "NR-2026-000123",
  "provider": "hopnow",
  "offramp": {
    "fromAsset": "USDC",
    "toAsset": "USD",
    "fromAmountMinor": "10003250000",
    "toAmountMinor": "1000004",
    "fxRate": "0.99968",
    "roundingRule": "floor",
    "occurredAt": "2026-07-15T02:10:11.902Z"
  },
  "payout": {
    "amountMinor": "1000004",
    "currency": "USD",
    "exponent": 2,
    "bankRef": "FEDWIRE-20260715-00123",
    "occurredAt": "2026-07-15T02:14:02.000Z"
  },
  "fees": [
    { "type": "protocol_fee", "amountMinor": "1500000", "currency": "USDC", "exponent": 6, "collection": "deducted" },
    { "type": "payout_fee",   "amountMinor": "2500",    "currency": "USD",  "exponent": 2, "collection": "deducted" },
    { "type": "network_fee",  "amountMinor": "0",       "currency": "USDC", "exponent": 6, "collection": "absorbed" }
  ],
  "issuedAt": "2026-07-15T02:14:05.113Z",
  "txHash": "0x9c1d8f2e…",
  "finality": {
    "txHash": "0x9c1d8f2e…",
    "contractAddress": "0x7b2f…c41a",
    "chainId": 43113
  }
}
FieldTypeDescription
_schemaVersionstringBody-schema version — currently ezys-receipt-1. This is the freeze unit: it changes only when the field set changes, so branch your parser on it rather than on field presence
clientReferencestringYour reference key, echoed verbatim — the join key against your ledger
providerstringPayout provider that executed the delivery (normalized identifier)
offrampobject | nullThe FX conversion that actually happened, or null on a pure-fiat rail where no conversion took place. See Offramp block
payoutobjectThe fiat delivery that actually happened. See Payout block
feesarrayFees actually charged (or absorbed), itemized and kept separate from principal. An empty array means "explicitly no fees". See Fees
issuedAtstringWhen this normalized receipt was issued
txHashstringAnchoring transaction hash. Omitted until the anchor is confirmed
finalityobjectLedger anchor — txHash, contractAddress, chainId. Present under the same condition as txHash. See Anchoring

txHash and finality are response metadata, not body: the content hash that the anchor commits to is computed over the body without them, so the body is byte-identical before and after anchoring.

Offramp block

null when the execution had no FX conversion — a pure-fiat rail delivers without converting.

FieldTypeDescription
fromAsset / toAssetstringConversion pair (e.g. USDCUSD)
fromAmountMinor / toAmountMinorstringAmounts in minor units, integer strings — see Money & Amounts
fxRatestringExecuted conversion rate
roundingRulestringRounding applied — floor, per the Money & Amounts contract (residuals are ledgered, never silently lost)
occurredAtstringExecution time of the conversion

Payout block

FieldTypeDescription
amountMinorstringAmount delivered to the beneficiary, minor units
currencystringDelivery currency (ISO 4217)
exponentnumberDecimal places of currency — apply it to read amountMinor
bankRefstring | nullRail-level reference of the credit (e.g. wire reference). null when the provider returns none
occurredAtstringConfirmation time of the delivery

beneficiary is specified but not yet returned

A masked recipient block — nameMasked, accountLast4, accountHash; raw beneficiary data is never returned on this surface — is part of the intended contract but is not present in ezys-receipt-1. It arrives in a later body version, and _schemaVersion will change when it does. Do not code against it yet, and do not treat its absence as an error.

The accountHash will be a keyed, stable hash of the account alone: the same beneficiary account always yields the same hash, so consumers can group receipts by beneficiary without ever holding the raw account number. The key and its rotation handling are fixed with the implementation.

Fees

Each fees[] entry is one fee item:

FieldTypeDescription
typestringClosed set: protocol_fee — the Ezys network fee for executing the intent; payout_fee — the payout rail / provider charge; network_fee — on-chain gas for the settlement legs
amountMinorstringAmount in minor units of this item's currency — items may be denominated in different currencies, so sum only per currency
currencystringCurrency / ticker of this item. Not always ISO 4217 — network_fee is denominated in the input asset, so a token ticker such as USDC is a valid value
exponentnumberDecimal places of this item's currency — apply it to read amountMinor
collectionstringClosed set of two: deducted — taken in-flow from the transferred asset (transaction-fee style); absorbed — fronted by Ezys and reported for transparency, not charged

Two invariants:

  • Fees are never baked into the principal amounts in the offramp / payout blocks — the R-11 fee-separation guarantee.
  • FX spread is not a fee item. offramp.fxRate is the all-in executed rate; the spread is a component of that rate, not a separately charged fee. (A mid-rate reference for rate verification belongs to the quote surface, not the receipt.)

The protocol_fee is collected transaction-fee style — deducted from the input asset at settlement (collection: "deducted") — consistent with the network's decentralized execution model.

Anchoring

Each receipt's content hash is written to an on-chain anchor. The hash is computed over the receipt body excluding txHash and finality, so the body never has to reference the transaction that records it, and it stays byte-identical before and after the anchor confirms.

To verify a receipt independently: take the body as returned, drop txHash and finality, serialize it canonically (object keys sorted recursively; array order preserved), hash it, and compare against the anchor that finality points to.

Array order in fees is part of the hashed input — it is preserved exactly as issued.

Reconciliation

GET /v1/reconciliation?from={date}&to={date}

Aggregate view over a date range for ledger reconciliation: one row per execution (keyed by clientReference) with amounts, fees, and terminal status, plus range totals. Pagination and a fixed daily-cutoff variant (/v1/reports/reconciliation/daily?date=) are part of the final spec, aligned with the daily evidence-export milestone.

Reconciliation rows are derived from the same store as receipts.

Notes

  • All amounts are minor-unit integer strings with floor rounding — the full contract is Money & Amounts.
  • Receipts are immutable once issued, and there is exactly one per execution — this version has no revision, correction, or credit-note mechanism. The execution's own history stays on the Status API event stream; the receipt is neither edited nor reissued.
  • Each receipt is anchored on-chaintxHash points to that transaction. What the chain records is the receipt's content hash, not the body itself, so no recipient data ever reaches the chain. Recomputing the hash from the returned body verifies the receipt without trusting the API response alone — see Anchoring.
  • Retention target for receipt/event backfill is 90 days (subject to agreement).