Skip to main content
Every sandbox operation that touches the chain (faucet funding, mints, transfers, redemptions, and each leg of an Aro Mail letter) writes a transaction row the moment the API accepts the request and updates it as the chain confirms. A background worker executes fund, mint, transfer and redeem rows; the Aro Mail service runs its letter legs inline and reconciles them whenever the letter is read. Transactions are your audit trail and your polling target.

The transaction object

Lifecycle

  • Every transaction starts QUEUED. The POST returns 201 as soon as the row and its job are written; nothing has touched the chain yet, so txHash is null.
  • PENDING means the worker has broadcast the primary transaction and is waiting for the receipt; txHash and explorerUrl are populated.
  • Most types go QUEUED → PENDING → CONFIRMED (terminal) or → FAILED.
  • Redeems have a second leg: QUEUED → PENDING → SETTLING → SETTLED, with the payout hash in settlementTxHash. See Redeem cAROUSD.
  • Aro Mail legs are PENDING → CONFIRMED | FAILED rows written by the letter service (they skip QUEUED because the leg is broadcast inline), one per leg: LETTER_ESCROW (sender → escrow vault, written by POST /transfer/letter), LETTER_CLAIM (vault → recipient wallet, written by the claim), LETTER_REFUND (vault → sender address, written by void or expiry). The letter only changes status after the leg’s encrypted amount is verified, so a CONFIRMED leg with verified: false on the letter means “receipt in, verification pending”. See Aro Mail.
  • Terminal states are CONFIRMED, SETTLED, and FAILED; a terminal row never changes again.
FAILED means nothing moved on-chain for that row. A transaction is only marked failed when a pre-flight check rejected it before anything was broadcast, or when the on-chain call reverted (a revert moves no funds). If a submitted transaction is still unresolved, the row stays PENDING or SETTLING and the worker keeps driving it; it is never failed underneath you.

Asynchronous processing

Money-movement calls (/fund, /mint, /transfer, /redeem) do only the cheap, synchronous work in the request: authentication, validation, wallet ownership, amount parsing, the faucet cap, and a public-balance pre-check where one is possible. Everything that touches the chain or the FHE relayer (balance gates on encrypted funds, encryption, signing, broadcasting, receipt tracking, redeem settlement) runs in a dedicated background worker. Aro Mail is the exception: POST /transfer/letter and POST /transfer/letter/claim broadcast their leg inline, wait up to ~20 seconds for the receipt, and hand the rest to the letter’s own reconciliation, which probes the LETTER_* rows whenever the letter is read. An operator-scheduled sweep expires and refunds letters on time. The rule of thumb for what fails where: Typical timings on the sandbox: QUEUED → PENDING within about 30 seconds (the worker picks the job up within a couple of seconds and submits after its pre-flight reads), and PENDING → CONFIRMED one or two Ethereum blocks later. Redeem settlement adds the public-decryption round-trip in backed mode.
A transaction that stays QUEUED for more than 5 minutes indicates the worker is delayed or down. Your request is safe (it will resume automatically when the worker is back) but you should report it to your Aro Media contact with the transaction ID.

Polling

Reads are plain database reads; they do not advance anything. Poll until the status is one of the terminal values:
Poll GET /transactions/{id} until status is CONFIRMED, SETTLED, or FAILED. Test for membership in the terminal set; never test status != "PENDING", which would exit early on QUEUED and SETTLING.
Treat any status you do not recognise as in flight and keep polling. New intermediate statuses may be added; the terminal set (CONFIRMED, SETTLED, FAILED) is the stable contract.
If you have many transactions in flight, poll them in batches with the list endpoint instead of one request per row (one call per in-flight status: GET /transactions?status=QUEUED, ?status=PENDING, ?status=SETTLING). See Rate limits.

Listing and filtering

GET /transactions returns a paginated, filterable history:
Responses carry items, page, pageSize (default 20, max 100), and total. See Track transactions for patterns.

Receipts

Once a transaction has reached the chain, GET /transactions/{id}/nota produces a notarized Notareum receipt file; see .nota files. While the transaction is still in flight without a hash (QUEUED), the endpoint returns 409 receipt_unavailable with details.retryable: true; retry after it confirms. A transaction that FAILED before anything was submitted has no receipt and returns 400 receipt_unavailable with details.retryable: false.