Five lists read what you have and what happened, two your webhook endpoints and what Citadel posted to them, five what your organization and installation are made of: GET /transactions, GET /transfers, GET /wallets, GET /runs, GET /approvals and GET /webhook_deliveries are paged as described below. GET /webhook_endpoints, GET /vaults, GET /workflows, GET /networks, GET /assets and GET /prices are short and answer in one response.

One network per list

A list of data that lives on a network names it: ?network=mainnet. Without it the answer is 422 network_required; there is no “all networks”. A merchant on two networks reads each one separately. GET /networks takes no network: it is where the values come from. Neither do GET /webhook_endpoints and GET /webhook_deliveries: a webhook endpoint hears every network.

Pages

A page is limit rows, newest first, and the cursor of the next page:
Pass next_cursor back as cursor, with the same filters, until it is null. A cursor is opaque: do not build or edit one. A cursor this API did not issue answers 422 invalid_cursor; start again from the first page. The cursor is a position, not a snapshot. A walk to the end returns every row that was there when it started, each once. A row that arrives during the walk is not certain to show up in it: created_at is not the order in which rows become readable (see Catch up after missed webhooks), so a late row can land among the pages you have already read. To see what arrived since, read again from a margin before the newest row you have.

Filters

Each list documents its filters in the API reference. They combine: every filter you send must hold. A filter is one string: status, direction and type take several values separated by commas in it (status=confirmed,failed), and so does kind of GET /approvals. A filter sent as a list or an object (status[]=confirmed, {"type": ["run.failed"]}) answers 422; it is never ignored. from and to bound the time Citadel recorded the row (created_at): from inclusive, to exclusive, both ISO 8601 date-times such as 2026-10-01T00:00:00Z. A filter that names a wallet takes your reference on the network, or the wallet’s id, like GET /wallets/{reference}. An unknown wallet answers 404; a wallet id from another network, 422 network_mismatch. An unknown filter value (a status that does not exist) answers 422.

Catch up after missed webhooks

Webhooks tell you when to look; the lists are the record. After downtime, or when an endpoint was disabled, page each list from a point before where you stopped:
created_at is not the order in which rows become readable. Citadel records transfers in batches, the chains of one network in parallel, and stamps a row’s created_at when its batch starts, not when the batch completes. A deposit on one chain can therefore become readable after a deposit on another chain with a later created_at. So never start a catch-up at the exact created_at you stopped at: start 15 minutes before it, and deduplicate.
  • Deposits: GET /transfers?direction=in, from 15 minutes before the created_at of the last deposit you credited. Each row is the body of blockchain_event.confirmed: credit it the same way, deduplicated on tx_hash. The rows inside the margin are mostly deposits you already credited; the one you have not is the reason for the margin. To catch up on one token, add asset= with its contract from GET /assets.
  • Payouts: GET /transactions, from 15 minutes before you sent the oldest payout still in flight on your side. A transaction keeps its place in the list (the time Citadel accepted it) when its status changes, so a from later than that would miss it; the margin also covers a difference between your clock and Citadel’s. Each row is the body GET /transactions/{id} reads: a webhook you missed is a row whose status you have not seen yet.
  • Workflow runs: GET /runs, from 15 minutes before you sent the oldest event whose runs are still open on your side. A run keeps its place in the list (the time it started) when its status changes. To find the runs of one POST /events call, filter on its idempotency_key.
Overlap is harmless when you deduplicate; a gap is not. A batch takes seconds, so 15 minutes leaves room for a slow one; a wider margin costs only rows you skip as duplicates. To reconcile in full, walk a list to the end without from.

Keys limited to vaults

A key limited to vaults lists only what is theirs: the wallets in them, the transactions from those wallets and their approvals, the transfers that touch them, and the vaults themselves. In a transfer that also touches a wallet outside them, that leg reads wallet: null. GET /runs and GET /workflows refuse such a key with 403 vault_limited, and GET /approvals lists no runs for it: a workflow chooses its own wallets. GET /webhook_deliveries refuses it too: a webhook endpoint serves every vault.