Finality and reconciliation
A transfer isn't final when it is submitted; it becomes final when a ledger closes with it inside. This page explains that boundary, and how the issuer notices when the chain and the ledger disagree.
When a transfer is final
A transfer is final, and credited, on the first closed ledger that contains its transaction. Until that ledger closes, the transaction is only submitted; it can still expire and be rebuilt.
Each signed envelope moves through its own small state machine: built → signed → submitted → included or expired. A retry resubmits the same signed envelope until it is included or expires; nothing is rebuilt early.
After max_time passes, a lookup by hash decides the outcome: included if the transaction is on chain, or expired if it isn't found and the latest ledger's close time is past max_time. Only a confirmed expired allows a rebuild with a new sequence number.
Time bounds
An envelope is built with min_time = now − 30 s and max_time = now + 60 s, a 90-second window that never exceeds the signer's 120-second limit.
The network checks those bounds against the ledger close time, which trails wall-clock time by a few seconds. That is why min_time = now is rejected as tx_too_early; the shared helper pads the start backwards so a fresh envelope is still valid when it reaches a ledger.
Watching the chain
The watcher polls the Platform asset's contract events from a stored cursor. Each event is inserted once, keyed by the RPC event id, so a replay changes nothing. A to_muxed_id in an event is matched to an open redeem; one that names a deposit route goes to suspense in Phase 1.
The watcher also stores the network passphrase and the last ledger close time. If the sequence goes backwards or the passphrase changes, it halts, pauses minting and raises a reset alert. Account changes that don't appear as events are caught by comparing account snapshots on each poll. That comparison does not yet cover signers or thresholds.
Reconciliation
Reconciliation compares what the ledger says with what the chain and the rails say. It runs every five minutes and on demand:
| Check | On break |
|---|---|
On-chain supply against supply_expected | Sev-1, pause mints |
supply_expected plus in-flight against reserves | Sev-1, pause mints |
| Ledger customer fiat against the rail balance | Warning, tracked as in-transit |
| Reserve source freshness | Warning |
A break that pauses mints waits for an operator to resume them, with a reason on the audit log.
Endpoints
GET /v1/transfers/{id}/timelineshows where a transfer is and what it is waiting for.GET /v1/events/{id}reads one ingested event, andGET /v1/events/{id}/deliveriesshows its webhook deliveries.POST /v1/events/{id}/redeliverretries a webhook delivery.POST /v1/sandbox/transfers/{id}/outcomeforces an outcome for a sandbox transfer.