Skip to content

Status API

Availability: GET /v1/executions/{reference} is implemented and deployed; GET /v1/intents/{intentId}/status is still coming soon. Sandbox host rollout (api.ezys.sh) is in progress, so the surface is not yet reachable from the sandbox base URL. This page is the contract of record.

Query the current state and full transition history of a single execution — one end-to-end fill spanning up to three phases: intent (acceptance), offramp (JIT FX conversion), and payout (fiat delivery to the beneficiary).

This API is the authoritative source for resolving timeouts and undetermined results: if you miss a submission response or end up with an undetermined outcome, re-query here rather than re-submitting. It returns both the aggregate status and the per-phase event history, so callers can reconstruct exactly where an execution stands.

Get execution status

GET /v1/executions/{reference}

Returns the aggregate status of one execution, with the per-phase transition history (events[]) and, when failed, a structured failure block.

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

Path & query parameters

ParameterInRequiredDescription
referencepathYesYour own reference (clientReference, echoed from submission) or the Ezys-issued execution ID
refTypequeryNoclientReference (default) or executionId. Selects which namespace {reference} is resolved in — resolution is explicit, there is no prefix-based inference. An unrecognized value returns 400

Response

json
{
  "clientReference": "NR-2026-000123",
  "reference": "INT-2F8A3B1C",
  "providerReference": "whn_pay_9f2c1d",
  "status": "in_progress",
  "phase": "payout",
  "events": [
    {
      "phase": "intent",
      "state": "initiated",
      "occurredAt": "2026-07-15T02:10:04.113Z",
      "observedAt": "2026-07-15T02:10:04.113Z",
      "isTerminal": false,
      "isReversible": false
    },
    {
      "phase": "offramp",
      "state": "completed",
      "occurredAt": "2026-07-15T02:10:11.902Z",
      "observedAt": "2026-07-15T02:10:12.048Z",
      "isTerminal": true,
      "isReversible": false
    },
    {
      "phase": "payout",
      "state": "initiated",
      "occurredAt": "2026-07-15T02:10:14.330Z",
      "observedAt": "2026-07-15T02:10:15.101Z",
      "isTerminal": false,
      "isReversible": false
    }
  ],
  "failure": null,
  "finality": null
}
FieldTypeDescription
clientReferencestring | nullYour reference key, echoed back verbatim on every read surface (status, receipts, reconciliation)
referencestringEzys-issued execution ID
providerReferencestring | nullDownstream rail / payout-provider reference, when one exists
statusstringAggregate status of the execution — one of in_progress, completed, failed. Derived from the execution lifecycle together with the payout leg, not from the last entry in events[]
phasestringThe phase the execution is currently in: intent, offramp, or payout
eventsarrayOrdered transition history. See Events
failureobject | nullPresent when the execution failed. See Failure
finalityobject | nullLedger anchor of this execution's receipt — txHash, contractAddress, chainId. null until the receipt has been anchored on-chain. The same anchor the Receipts API returns

Events

Each events[] entry is one observed state transition:

FieldTypeDescription
phasestringintent, offramp, or payout
statestringPhase-specific state. See Phase & state model
occurredAtstringWhen the transition happened at the source (e.g. vendor webhook created timestamp)
observedAtstringWhen Ezys recorded it. occurredAtobservedAt; a large gap indicates delayed vendor notification
isTerminalbooleanEzys will not advance this event's phase any further — the signal to stop polling that phase. Scoped to the phase, not to the execution as a whole
isReversiblebooleanWhether this API's normalized state machine formally permits a transition to reversed from this state. Not a prediction, and not a guarantee that the underlying rail supports reversal — see Payout finality

Events are appended, never rewritten. Out-of-order vendor delivery is resolved internally — the recorded state never regresses.

Phase & state model

events[].state is a closed vocabulary shared by all phases — exactly these five values:

StateTerminalReversible
initiatednono
in_progressnono
completedyesonly in the payout phase
failedyesno
reversedyesno

isTerminal is scoped to the phase the event belongs to, not to the execution as a whole — offramp.completed is terminal for the off-ramp leg, and payout events follow it. It reports that Ezys will not advance that phase any further, so you can stop polling it.

Payout finality

A payout event with state: completed and isTerminal: true is a delivered payout — the funds reached the beneficiary and Ezys does not move that payout again. Settle on that combination.

isReversible describes the shape of the state machine, not an expectation about a given payout. completed → reversed is the single edge that leads to reversed, so payout + completed is the only state the flag can be true for. It is not a forecast that a reversal will happen, and not a guarantee that a payout rail supports chargeback or refund — no supported rail records that transition today. Do not hold a delivered payout open in your books on the strength of this flag.

Not every phase emits every value; the table above is the closed union across phases.

  • The offramp track is driven internally by the JIT FX stages (quote → trade → filled) but is exposed using the common lifecycle states above. Event order within the phase is stable.
  • rejected is not an event-stream state. A synchronously rejected submission (HTTP 422, status: "rejected" — see Error Handling & Retries) never starts executing, so it never enters the event store: no events, and this endpoint returns 404 for it.

Failure

When status is failed, the failure block identifies where and why:

json
{
  "failure": {
    "stage": "payout",
    "code": "beneficiary_account_closed",
    "message": "Beneficiary bank rejected the credit: account closed.",
    "retryable": false
  }
}
FieldTypeDescription
stagestringPhase that failed: offramp or payout
codestringStable, machine-readable code (provider-original codes are mapped, not passed through raw)
messagestringHuman-readable detail
retryablebooleanWhether re-submitting the same request can succeed. false codes are deterministic rejections

Get intent status (lightweight)

GET /v1/intents/{intentId}/status

A lightweight, intent-only status check — returns the aggregate status without the full events[] history. This endpoint is planned for public exposure (reduced authentication scope relative to the execution endpoint above; the exact auth split is defined with the gateway authentication work).

Errors

Failed lookups return errors in the format described in Response Format. An unknown reference — or one that belongs to a different API key — returns 404; the two are indistinguishable by design, so a reference cannot be probed for existence across tenants. refType defaults to clientReference when omitted; an unrecognized refType value returns 400.

Notes

  • All timestamps are ISO-8601 UTC strings.
  • clientReference is echoed on every read surface — status, receipts, and reconciliation — so your ledger can join on a single key.
  • Batch lookup (multiple references per call) is planned alongside the reconciliation surface — see Receipts & Reconciliation.