Tracking Transfers
Follow a transfer from submission to a settled outcome.
Once you have submitted an execution, you track it here. There is nothing to
extract from a receipt and no identifier to translate: the quoteId you priced,
the transferId the execution returned, and the orderId you track here are
the same value.
Reading transfer status needs no API key, unlike quoting and executing.
The two endpoints
| Endpoint | Use |
|---|---|
GET /v1/transfers/{orderId} | One transfer, by order id |
GET /v1/transfers | A newest-first, keyset-paginated list |
const BASE = "https://api.staging.across.to";
const res = await fetch(`${BASE}/v1/transfers/${orderId}`);
const transfer = await res.json();Reading status
status is the field that matters. It carries five properties, and the two
that decide your polling logic are isTerminal and finalized.
| Field | Meaning |
|---|---|
state | Coarse outcome: pending, completed, refunded, failed |
reason | Granular reason — see the mapping below |
description | A support-safe sentence you can show a user directly |
isTerminal | Across will take no further action on this transfer |
finalized | The chain state behind it can no longer reorg |
Each reason maps to exactly one state, so you can branch on whichever
granularity you need:
state | reason |
|---|---|
pending | awaiting_auction, funds_received_awaiting_auction, awaiting_origin_execution, awaiting_destination_execution, awaiting_delivery |
completed | filled |
refunded | refund_confirmed |
failed | expired, source_transfer_failed |
Branch on state and reason. Never parse description — it is written for
humans and its wording is not stable.
Polling
Poll until isTerminal is true, then slow down rather than stopping outright.
const BASE = "https://api.staging.across.to";
async function pollTransfer(orderId: string, intervalMs = 10_000, maxAttempts = 60) {
for (let i = 0; i < maxAttempts; i++) {
const res = await fetch(`${BASE}/v1/transfers/${orderId}`);
const transfer = await res.json();
const { state, reason, isTerminal } = transfer.status;
console.log(`[${i + 1}] ${state} / ${reason}`);
if (isTerminal) return transfer;
await new Promise((r) => setTimeout(r, intervalMs));
}
throw new Error("Polling timed out");
}A terminal transfer is not necessarily a final one. isTerminal means
Across is done; finalized means the chain can no longer reorg underneath
it. Between the two, an outcome can still change. The API corrects itself either way, so the safe pattern is to keep polling at a low frequency — once
a minute or so — until finalized, rather than stopping at isTerminal and
showing a result that may move.
- Poll no faster than every 10 seconds while pending. Status lags the chain by a few seconds, so tighter polling returns the same answer.
- After
isTerminal, drop to 1–5 minutes untilfinalized. - Set an overall timeout so a stuck transfer does not poll forever.
Listing transfers
GET /v1/transfers returns newest-first with keyset pagination. Every filter
below accepts multiple values, and repeating one widens the match rather than
narrowing it. limit defaults to 50 and is capped at 100 — a larger
value is clamped silently rather than rejected.
GET /v1/transfers?depositor=0xYourAddress&limit=25GET /v1/transfers?statusState=pending&statusState=failedGET /v1/transfers?fromSubmittedAt=2026-09-01T00:00:00Z&toSubmittedAt=2026-09-02T00:00:00ZOther filters: orderId, externalId, recipient, txnRef,
inputTokenAddress, outputTokenAddress, inputChainId, outputChainId.
Unknown query parameters are rejected, not ignored — a typo returns 400
with code: "request.invalid_param" naming the offending param.
Each page carries pagination.nextCursor and pagination.nextUrl. nextUrl is
a relative path with your filters already applied, so you can append it to the
base URL directly; nextCursor is the same position as a bare token. Either
works — the loop below uses nextCursor.
let cursor: string | undefined;
do {
const qs = new URLSearchParams({ depositor, limit: "50" });
if (cursor) qs.set("cursor", cursor);
const page = await (await fetch(`${BASE}/v1/transfers?${qs}`)).json();
handle(page.transfers);
cursor = page.pagination?.nextCursor;
} while (cursor);The transaction ledger
transfer.transactions is a flat, cross-chain ledger of every transaction
observed for the order. Each entry has a type naming the leg it belongs to:
origin_action, funding, origin_entry, settlement_fill,
destination_swap, destination_action, destination_delivery, refund.
destination_delivery.txnRef settles a batch of unrelated orders. Never
treat it as an identifier for this transfer.
Next
GET /v1/transfers/{orderId}— full response schemaGET /v1/transfers— every filter and pagination detail