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

EndpointUse
GET /v1/transfers/{orderId}One transfer, by order id
GET /v1/transfersA 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.

FieldMeaning
stateCoarse outcome: pending, completed, refunded, failed
reasonGranular reason — see the mapping below
descriptionA support-safe sentence you can show a user directly
isTerminalAcross will take no further action on this transfer
finalizedThe chain state behind it can no longer reorg

Each reason maps to exactly one state, so you can branch on whichever granularity you need:

statereason
pendingawaiting_auction, funds_received_awaiting_auction, awaiting_origin_execution, awaiting_destination_execution, awaiting_delivery
completedfilled
refundedrefund_confirmed
failedexpired, 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.

poll-transfer.ts
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 until finalized.
  • 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=25
GET /v1/transfers?statusState=pending&statusState=failed
GET /v1/transfers?fromSubmittedAt=2026-09-01T00:00:00Z&toSubmittedAt=2026-09-02T00:00:00Z

Other 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

On this page