Appearance
Status API
Availability:
GET /v1/executions/{reference}is implemented and deployed;GET /v1/intents/{intentId}/statusis 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
| Parameter | In | Required | Description |
|---|---|---|---|
reference | path | Yes | Your own reference (clientReference, echoed from submission) or the Ezys-issued execution ID |
refType | query | No | clientReference (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
}| Field | Type | Description |
|---|---|---|
clientReference | string | null | Your reference key, echoed back verbatim on every read surface (status, receipts, reconciliation) |
reference | string | Ezys-issued execution ID |
providerReference | string | null | Downstream rail / payout-provider reference, when one exists |
status | string | Aggregate 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[] |
phase | string | The phase the execution is currently in: intent, offramp, or payout |
events | array | Ordered transition history. See Events |
failure | object | null | Present when the execution failed. See Failure |
finality | object | null | Ledger 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:
| Field | Type | Description |
|---|---|---|
phase | string | intent, offramp, or payout |
state | string | Phase-specific state. See Phase & state model |
occurredAt | string | When the transition happened at the source (e.g. vendor webhook created timestamp) |
observedAt | string | When Ezys recorded it. occurredAt ≤ observedAt; a large gap indicates delayed vendor notification |
isTerminal | boolean | Ezys 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 |
isReversible | boolean | Whether 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:
| State | Terminal | Reversible |
|---|---|---|
initiated | no | no |
in_progress | no | no |
completed | yes | only in the payout phase |
failed | yes | no |
reversed | yes | no |
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.
rejectedis not an event-stream state. A synchronously rejected submission (HTTP422,status: "rejected"— see Error Handling & Retries) never starts executing, so it never enters the event store: no events, and this endpoint returns404for 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
}
}| Field | Type | Description |
|---|---|---|
stage | string | Phase that failed: offramp or payout |
code | string | Stable, machine-readable code (provider-original codes are mapped, not passed through raw) |
message | string | Human-readable detail |
retryable | boolean | Whether re-submitting the same request can succeed. false codes are deterministic rejections |
Get intent status (lightweight)
GET /v1/intents/{intentId}/statusA 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.
clientReferenceis 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.
