# Transactions

Read the ledger's transactions, newest first or filtered from all of history, and one transaction by its hash, with its signature, status and effects.

These endpoints only read. To send a transaction, sign it and `POST /api/tx`: see [Submitting transactions](/transactions/submitting). Every transaction type and its body is on [Transaction types](/transactions/types).

## GET /api/txs {#list}

```endpoint try
GET /api/txs?limit=2
```

Without filters: the newest transactions across the network, from the ledger's recent list (fast; it holds only the most recent ones). With any of `type`, `status`, `token`, `q`, `period`, `sort` or `cursor`: a filtered query that can reach every transaction ever sealed, one page at a time.

```bash tab=curl
curl "https://mlx.app/api/txs?type=node,swap&status=success&limit=10"
```

```js tab=JavaScript
const url = new URL("https://mlx.app/api/txs");
url.search = new URLSearchParams({ type: "node,swap", status: "success", limit: "10" });
const page = await (await fetch(url)).json();
for (const r of page.txs) console.log(r.hash, r.tx.body.type, r.tx.from);
// page.next: pass it back as cursor for the next page (null at the end)
```

```python tab=Python
import requests

page = requests.get("https://mlx.app/api/txs", params={"type": "node,swap", "status": "success", "limit": 10}).json()
for r in page["txs"]:
    print(r["hash"], r["tx"]["body"]["type"], r["tx"]["from"])
```

| Parameter | Type | Notes |
| --- | --- | --- |
| `limit` | integer | Page size. The route has a default and a maximum. |
| `type` | list | Comma-separated categories: `transfer`, `swap` (spot buys and sells), `node` (seat purchases, grants and transfers), `account` (new wallets), `deposit`, `withdraw` (requests and settlements), `device` (Mac links), `system` (everything the protocol signs: genesis, compute rewards, issues, commissions…). Unknown values are ignored. |
| `status` | `success` or `failed` | Only that outcome. |
| `token` | `MLX`, `USDC` or `USDT` | Only transactions that name or move that token. |
| `q` | string | Part of an account name, or the first hex characters of a hash. An **exact** account name (`luca` or `luca.mlx`) switches to that account's own history, which is much faster. |
| `period` | `1h`, `24h`, `7d`, `30d` or `all` | Only transactions from that window. |
| `sort` | `new`, `old` or `amount` | Newest first (default), oldest first, or largest first in `token` (MLX if no token is given). |
| `cursor` | string | The `next` value from the previous page. |

### Unfiltered response

```json title="Response (GET /api/txs?limit=1)"
{
  "txs": [
    {
      "hash": "e74fb256b0faa50d1863fa8064e49540f74543c56a9c5d0aecec88cda6eacae7",
      "tx": {
        "chain": "mlx-1",
        "from": "luca.mlx",
        "nonce": 2,
        "ts": 1792519094341,
        "pk": "53fbe3a2e4ee9ee2ad907174562e8f2fc9b2bb3e1074f8ce274935439422c4ab",
        "body": {
          "type": "buy_node",
          "tier": "S",
          "device": { "class": "mac", "screen": "1800x1169@2", "cores": 8, "appleSilicon": true, "model": "MacBook", "chip": "M5" },
          "pay": "WALLET"
        },
        "sig": "7f625684e34a5961706ee175f57fea05caf3a67cc48f1b7445c41474aa834d4d1efa0ec077d1116b5d6e51b20bfc0dbc022c9300fa5f630acd8a2a0e850ef70f"
      },
      "status": "success",
      "receivedAt": 1792519094663,
      "effects": [
        { "kind": "transfer", "token": "USDC", "from": "luca.mlx", "to": "treasury.mlx", "amount": "250000000", "note": "Core seat payment" },
        { "kind": "mint_node", "to": "luca.mlx", "node": 2, "note": "Core seat", "until": 1793383094698 }
      ],
      "height": 80,
      "index": 0
    }
  ]
}
```

### Filtered response

A filtered page has the same `txs` list plus how far the query got:

```json title="Response (GET /api/txs?type=swap&limit=1), txs left out"
{
  "mode": "blocks",
  "txs": [],
  "next": "388.9999",
  "examined": 11,
  "reached": 389,
  "head": 399,
  "all": 400
}
```

| Field | Type | Notes |
| --- | --- | --- |
| `mode` | string | `blocks` (walked the blocks), `account` (an exact account name in `q`: paged its history) or `largest` (`sort=amount` across the network). |
| `next` | string or null | Pass it back as `cursor`. `null` means there is nothing more. |
| `examined` | integer | Transactions looked at in this call. |
| `reached` | integer or null | The last block height looked at (`null` in account mode). |
| `head` | integer | Chain height when the query ran. |
| `all` | integer | Transactions on the whole ledger. |
| `account` | string | Account mode only: the account paged. |
| `total` | integer | Account mode with `sort=old` or `amount`, and `largest` mode: how many match. |
| `token` | string | With `sort=amount`: the token ranked by. |
| `complete` | boolean | `largest` mode: whether the window it ranked covered every matching transaction. |

A `blocks` query looks at a bounded number of transactions per call and stops, so a page can come back short or empty with `next` still set: keep going until `next` is `null`. Account mode reads only the newest transactions the ledger keeps per account (see [Accounts](/network/accounts)); `largest` mode ranks the newest matching transactions within a window, and says whether that was all of them in `complete`.

## GET /api/tx/{hash} {#tx}

```endpoint try
GET /api/tx/e74fb256b0faa50d1863fa8064e49540f74543c56a9c5d0aecec88cda6eacae7
```

One transaction record, plus the hash and time of the block that sealed it. Not cached: a pending transaction can change.

| Parameter | Type | Notes |
| --- | --- | --- |
| `hash` | string (path) | The full 64-character hex hash. A prefix isn't looked up (use `q` on `/api/txs`). |

The response is the record (below) with two more fields:

| Field | Type | Notes |
| --- | --- | --- |
| `blockHash` | string | Hash of the block at `height`. Absent while the transaction is pending. |
| `blockTs` | integer | That block's time, ms. |

```json title="Response, shortened (the full record is above)"
{
  "hash": "e74fb256b0faa50d1863fa8064e49540f74543c56a9c5d0aecec88cda6eacae7",
  "tx": { "chain": "mlx-1", "from": "luca.mlx", "nonce": 2, "body": { "type": "buy_node", "tier": "S", "pay": "WALLET" } },
  "status": "success",
  "height": 80,
  "index": 0,
  "blockHash": "c740d89fbe8bc3b0df7b6897a3bb62a01be96f6c564e7f2be14be945d61c3d90",
  "blockTs": 1792519094698
}
```

## The transaction record {#record}

Every endpoint that returns transactions (`/api/txs`, `/api/tx/{hash}`, `/api/block/{height}`, `/api/account/{name}?txs=`) uses this format.

| Field | Type | Notes |
| --- | --- | --- |
| `hash` | string | SHA-256 of the transaction's canonical JSON without `sig`. See [Keys and signing](/transactions/keys-and-signing). |
| `tx` | object | The signed transaction as it was sent: `chain`, `from`, `nonce`, `ts`, `pk` (signer's public key), `body` and `sig`. Key order inside `tx` can differ between responses; only the canonical form is hashed. |
| `tx.body` | object | What it does: `type` plus that type's fields. See [Transaction types](/transactions/types). |
| `status` | string | `pending`, `success` or `failed`. A failed transaction is still sealed in a block, with nothing applied. |
| `error` | string | Why it failed, in the ledger's words. Only on failed transactions. |
| `height`, `index` | integer | The block it is in and its place in that block. Absent while pending. |
| `effects` | array | What it changed, in order. Each has a `kind` (`transfer`, `mint_node`, `reward`, `account`, `lock` or `seat`, a seat that changed hands) and, as they apply, `token`, `from`, `to`, `amount` (base units), `node` (a seat id), `tier` (on `seat`), `note` and `until` (when locked MLX unlocks, ms). |
| `receivedAt` | integer | When the sequencer received it, ms. |
| `sold` | object | Only on a seat paid from the wallet that sold unlocked MLX for part of the price: `mlx` and `usd`, base units. |
| `nameHidden` | `true` | On a Mac link whose name was replaced (below). |

The genesis transaction (block 0) has `from: "genesis"` and a body of type `genesis` listing every allocation. Transactions signed by the protocol itself (compute rewards and the like) have `from: "sequencer"` and the sequencer's key in `pk`.

## Mac names stay private {#mac-names}

A Mac's own name (what its owner called it) is never published. Every public read passes through the same filter:

- A `bind_device` record shows the **kind of Mac** in `body.name` ("MacBook Pro", "Mac mini", or "Mac" when nothing says), its `proof` is removed, and the record carries `nameHidden: true`. Its effect note reads "Linked a MacBook Pro".
- An `unbind_device` record's note reads "Unlinked a MacBook Pro"; a Mac that moved wallets reads "A MacBook Pro moved to luca.mlx".
- Accounts list their Macs by kind too: see [Accounts](/network/accounts).

Because the name is part of what the Mac and the wallet signed, a `nameHidden` record's hash and signature can't be checked from the public copy. Its hash is still in its block's `txs` and `txRoot`, so the block itself checks out. The owner's own apps still see the names.

```json title="A Mac link as the public sees it"
{
  "hash": "a894d03cd323972221d19dadd41b89f29eb7b23efe105207b4743793b1e0d5ce",
  "tx": {
    "chain": "mlx-1",
    "from": "luca.mlx",
    "nonce": 1,
    "ts": 1792314044027,
    "pk": "53fbe3a2e4ee9ee2ad907174562e8f2fc9b2bb3e1074f8ce274935439422c4ab",
    "body": {
      "type": "bind_device",
      "device": "02f9b1f9aec77a141ae279d386cc1cd8f9c940b3102329b27af9ae5c018cfd185d",
      "name": "MacBook Pro",
      "chip": "M5",
      "memoryGB": 16,
      "os": "macOS 26.6.2",
      "ts": 1792314016229
    },
    "sig": "fb6361c207c182dad89a633e32fda0bcd5cb1141562fb4a7a8bdaf1961d913ea0449e5c07abddcc5239cb3614416a663d9fd66004d82e7bba49c4773fbe9e901"
  },
  "status": "success",
  "receivedAt": 1792314044592,
  "effects": [{ "kind": "account", "to": "luca.mlx", "note": "Linked a MacBook Pro" }],
  "height": 2,
  "index": 0,
  "nameHidden": true
}
```

## Errors

| Status | When |
| --- | --- |
| 404 | `/api/tx/{hash}`: no transaction with that hash (`"Transaction not found"`), including a hash that isn't 64 hex characters. |
| 500 | The ledger couldn't be read. |

`/api/txs` never answers 400: unknown filter values fall back to their defaults.
