# Prepare a transfer

`POST /api/market/transfer/prepare`

Step 1 of every wallet tool: extract rare sats, send UTXOs or only their rare sats, send N sats of a type, chop ranges, consolidate plain bitcoin, pay many recipients, or speed up or cancel a pending transfer built here. The server checks every UTXO (yours, unspent, confirmed; its tracked sats, inscriptions and runes from the rare-sat index and the ord indexer), lays out the outputs, funds the network fee and the flat tools fee only from plain bitcoin of your payment address, checks the unsigned transaction against its invariants (no tracked, inscribed or rune sat is paid as fee, every tracked sat lands in its planned output, …) and returns it to sign with a summary. Nothing is reserved; the plan is held 10 minutes.

#### Request body

`content-type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `owner` | object | yes | The connected wallet: Xverse-style wallets have two addresses, UniSat-style one (give it as both). Both keys are checked against their addresses. |
| `owner.payment_address` | string | yes | Pays the fees and gets the change and plain slices. Taproot, native SegWit or nested SegWit. |
| `owner.payment_public_key` | string | yes | Its public key, hex (Taproot: the internal key, x-only or compressed; SegWit: compressed). |
| `owner.ordinals_address` | string | yes | Holds the rare sats; the default destination. May equal `payment_address`. |
| `owner.ordinals_public_key` | string | yes | Its public key, hex. |
| `kind` | string | yes | What to do. One of: `extract`, `send`, `send_n`, `chop`, `consolidate`, `multisend`, `speedup`, `cancel`. |
| `fee_rate` | number | yes | sat/vB, at least `minimum` of `GET /api/market/fees`. (max 5000) |
| `items` | array of object | no | `extract`, `send`, `chop`; `consolidate` (2 or more plain UTXOs, or absent: the server picks); optional for `send_n` (limits its pick). (1-50 items) |
| `items[].outpoint` | string | yes | `txid:vout`. |
| `items[].sats` | array of integer \| null | no | `extract` only: the tracked sats (absolute sat numbers) to extract; `null` or absent: all of them. A sat inside a block 9 range picks the whole range. (0-10000 items) |
| `destination` | string | no | Where the moved sats go. Default: your ordinals address (your payment address for `consolidate`). P2TR, P2WPKH, P2SH, P2WSH or P2PKH; never the fee address. |
| `plain_to` | string | no | `extract`: where slices holding no tracked sat go (default `payment`). One of: `payment`, `source`. |
| `split` | object \| object | no | `extract` only: split each UTXO's sats of one type into chunks instead of separating each rare sat (see [Extract rare sats](https://raresats.net/docs/guide-extract.md#split-into-chunks)). Each chunk is an output of its own to `destination` (a chunk whose sats lie on both sides of a whole range, block 9, is cut around it: the range keeps an output of its own); the other rare sats of the UTXO stay home or ride along with a chunk to your own address, never to plain change. The items take no `sats`. With several items, one that cannot make 2 chunks is skipped (`summary.skipped`); 2,000 sats of the type at most per request, and per UTXO at most 2,000 of them and its other tracked runs and exotic intervals together (`too_many_items`), and the outputs as laid out (chunks too close together share one) must fit in 100 (`too_many_outputs`). |
| `split.type` | string | yes | The exact type combination to chunk (`uncommon`, `black+uncommon`): 1 to 8 lowercase types of 1-40 letters, digits or `_`, joined with `+`, as `send_n` matches it (`uncommon` does not take a `black+uncommon` sat); repeats count once. Never a whole range (`block9`, `450x`): `chop` cuts those. |
| `split.per_output` | integer | no | Sats of the type per output, in offset order; the last chunk holds the remainder. Exactly one of `per_output` and `chunks`. (min 1, max 2000) |
| `split.chunks` | integer | no | How many chunks: near-equal, the first ones one larger. Exactly one of `per_output` and `chunks`. (min 2, max 100) |
| `split.trim` | boolean | no | `true`: the plain sats between two chunks go back as plain change (to `plain_to`) when they make an output of their own; smaller gaps ride along with a chunk. `false` (default): each chunk's output runs from its first sat to the next chunk; other rare or exotic sats between two chunks ride along too when `destination` is your own address (a whole range never does: it keeps an output of its own). |
| `mode` | string | no | `send`: the UTXOs as they are, or only their rare sats (default `whole`). One of: `whole`, `rare`. |
| `consolidate` | boolean | no | `send` `whole`: one output for all the UTXOs instead of one each. |
| `type` | string | no | `send_n`: an exact type combination (`uncommon`, `black+uncommon`) or a range type (`block9`, `450x`). |
| `count` | integer | no | `send_n`: how many sats. (min 1) |
| `chop_size` | integer | no | `chop`: cut at sats divisible by this. (min 330, max 100000000) |
| `sends` | array of object | no | `multisend`: the payments. (1-50 items) |
| `sends[].address` | string | yes |  |
| `sends[].value` | integer | yes | Sats. |
| `txid` | string | no | `speedup`, `cancel`: the pending wallet transfer to replace. |
| `confirm_payment_destination` | boolean | no | Allow sending rare sats, inscriptions or runes to your own payment address (otherwise `payment_destination`). |

#### Example request

```bash title="cURL"
curl -X POST 'https://raresats.net/api/market/transfer/prepare' \
  -H 'content-type: application/json' \
  -d '{
  "owner": {
    "payment_address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
    "payment_public_key": "039b3b694b8fc5b5e07fb069c783cac754f5d38c3e08bed1960e31fdb1dda35c24",
    "ordinals_address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
    "ordinals_public_key": "03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"
  },
  "kind": "extract",
  "items": [
    {
      "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0"
    }
  ],
  "fee_rate": 3
}'
```
```js title="JavaScript"
const response = await fetch('https://raresats.net/api/market/transfer/prepare', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    "owner": {
      "payment_address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
      "payment_public_key": "039b3b694b8fc5b5e07fb069c783cac754f5d38c3e08bed1960e31fdb1dda35c24",
      "ordinals_address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
      "ordinals_public_key": "03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"
    },
    "kind": "extract",
    "items": [
      {
        "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0"
      }
    ],
    "fee_rate": 3
  })
});
const data = await response.json();
```
```python title="Python"
import requests

data = requests.post("https://raresats.net/api/market/transfer/prepare", json={
    "owner": {
        "payment_address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "payment_public_key": "039b3b694b8fc5b5e07fb069c783cac754f5d38c3e08bed1960e31fdb1dda35c24",
        "ordinals_address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "ordinals_public_key": "03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"
    },
    "kind": "extract",
    "items": [{
        "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0"
    }],
    "fee_rate": 3
}).json()
```

#### Response `200`

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The prepared transfer, for submit. |
| `psbt` | string | The PSBT to sign, base64. Every input carries `witnessUtxo` (and `tapInternalKey` / `redeemScript` / `nonWitnessUtxo` as its type needs); no `sighashType`. |
| `sign` | array of object | Every input, with the address whose key signs it. Sign with the default sighash. |
| `sign[].index` | integer | The input to sign. |
| `sign[].address` | string | The address whose key signs it. |
| `sign[].sighash` | integer | The sighash type to sign with, when it is not the default: 131 (`0x83`, SIGHASH_SINGLE\|ANYONECANPAY) for listings. Absent on purchase inputs: sign with the default (SIGHASH_DEFAULT for Taproot, SIGHASH_ALL for SegWit v0). |
| `summary` | object | The plan as people read it: what each input holds, what each output gets and where it goes, the fees, the checks run on the unsigned transaction, and the warnings. |
| `summary.kind` | string | The kind. |
| `summary.network_fee` | integer | Sats, paid from your plain bitcoin. |
| `summary.tools_fee` | integer | The flat raresats.net fee (`tools.fee_sats`; 0 on a speed-up or cancel, which keep the original's fee output). |
| `summary.fee_rate` | number | sat/vB. |
| `summary.vsize` | integer | Estimated vbytes (an upper bound). |
| `summary.change` | integer | Sats back to your payment address as change. |
| `summary.total_cost` | integer | `network_fee + tools_fee`. |
| `summary.leaves_wallet` | integer | Sats paid to addresses that are not yours, other than the fees. |
| `summary.destination` | string \| null | The destination address. |
| `summary.external` | boolean | The destination is not one of your addresses. |
| `summary.inputs` | array of object | Every input: your UTXOs to move (`source`), plain funding (`funding`), or plain UTXOs (`plain`). |
| `summary.inputs[].index` | integer |  |
| `summary.inputs[].outpoint` | string |  |
| `summary.inputs[].value` | integer |  |
| `summary.inputs[].role` | string | One of: `source`, `funding`, `plain`. |
| `summary.inputs[].address` | string |  |
| `summary.inputs[].owner_role` | string | One of: `ordinals`, `payment`. |
| `summary.inputs[].items` | array of object | Its tracked sats. |
| `summary.inputs[].items[].sat` | integer | The first sat. |
| `summary.inputs[].items[].len` | integer | How many sats (1, or a range). |
| `summary.inputs[].items[].types` | array of string | Its sat types. |
| `summary.inputs[].items[].input` | integer | The input it comes from. |
| `summary.inputs[].items[].offset` | integer | Its offset in that input. |
| `summary.inputs[].items[].at` | integer \| null | Its offset in this output. |
| `summary.inputs[].items[].selected` | boolean | Picked to move (`false`: it stays where it was). |
| `summary.inputs[].inscriptions` | integer | How many inscriptions it carries. |
| `summary.inputs[].runes` | boolean |  |
| `summary.outputs` | array of object | Every output, in order. |
| `summary.outputs[].index` | integer |  |
| `summary.outputs[].value` | integer |  |
| `summary.outputs[].address` | string |  |
| `summary.outputs[].role` | string | `dest`: moved sats; `return`: back to a source's address; `plain_return`: a plain slice to your payment address; `tools_fee`; `change`. |
| `summary.outputs[].to` | string | Whose address it pays. One of: `ordinals`, `payment`, `source`, `external`, `fee`. |
| `summary.outputs[].parts` | array of object | The input sats it receives, first in first out: `[from, to)` of input `input`, at offset `at`. |
| `summary.outputs[].parts[].input` | integer |  |
| `summary.outputs[].parts[].from` | integer |  |
| `summary.outputs[].parts[].to` | integer |  |
| `summary.outputs[].parts[].at` | integer |  |
| `summary.outputs[].parts[].kind` | string |  |
| `summary.outputs[].items` | array of object | The tracked sats it receives. |
| `summary.outputs[].items[].sat` | integer | The first sat. |
| `summary.outputs[].items[].len` | integer | How many sats (1, or a range). |
| `summary.outputs[].items[].types` | array of string | Its sat types. |
| `summary.outputs[].items[].input` | integer | The input it comes from. |
| `summary.outputs[].items[].offset` | integer | Its offset in that input. |
| `summary.outputs[].items[].at` | integer \| null | Its offset in this output. |
| `summary.outputs[].items[].selected` | boolean | Picked to move (`false`: it stays where it was). |
| `summary.outputs[].rides_along` | array of object | Unpicked or exotic sats that ride along in an output to yourself. |
| `summary.outputs[].rides_along[].input` | integer |  |
| `summary.outputs[].rides_along[].from` | integer |  |
| `summary.outputs[].rides_along[].to` | integer |  |
| `summary.outputs[].rides_along[].at` | integer |  |
| `summary.outputs[].rides_along[].kind` | string |  |
| `summary.outputs[].kept` | array of object | Sats that are not plain going back home in this output: unpicked tracked sats and exotic ranges (`types`, e.g. vintage). Never shown as plain bitcoin. |
| `summary.outputs[].kept[].input` | integer |  |
| `summary.outputs[].kept[].from` | integer |  |
| `summary.outputs[].kept[].to` | integer |  |
| `summary.outputs[].kept[].kind` | string |  |
| `summary.outputs[].kept[].types` | array of string |  |
| `summary.outputs[].padding` | integer | Sats other than the picked ones in an extracted output. |
| `summary.outputs[].extracted` | boolean \| null | Whether the index will count it extracted ([Extraction](https://raresats.net/types/extraction)); `null` when it holds no tracked sat or the rule is unknown. |
| `summary.checks` | array of object | What the server verified on the unsigned transaction ("No rare sat is paid as fee", …). |
| `summary.checks[].rule` | string |  |
| `summary.checks[].ok` | boolean |  |
| `summary.checks[].label` | string |  |
| `summary.warnings` | array of object | Things to know before signing: `listed` (carries `listing_id`, `signature_public` and, when that is true, `recommended_fee_rate`: the fast rate), `offers_received`, `spends_offer_funding`, `payment_destination`, `not_taproot`, `external`, `fee_exceeds_freed`, `merged`, `chunk_not_extracted` (a split's chunk output above the extraction budget), `chunk_cut` (a split's chunk cut around a whole range), `not_aligned`. |
| `summary.warnings[].code` | string |  |
| `summary.warnings[].message` | string |  |
| `summary.warnings[].outpoint` | string |  |
| `summary.skipped` | array of object | Items left out of a multi-item extract or an automatic send N pick, with the reason (`already_extracted`, `split_chunk`, `inscribed`, `runes`, `unconfirmed`, `pending`, `untracked`, `nothing_to_separate`, `keep_in_destination`, `listed`, `offered`; for a split over several UTXOs `not_enough_sats` and `nothing_to_do`). |
| `summary.skipped[].outpoint` | string |  |
| `summary.skipped[].code` | string |  |
| `summary.skipped[].message` | string |  |
| `summary.replaces` | object \| null | A speed-up or cancel: the pending transaction it replaces (`fee` and `fee_rate` null for a void of one no longer in the mempool). |
| `summary.replaces.txid` | string |  |
| `summary.replaces.fee` | integer \| null |  |
| `summary.replaces.fee_rate` | number \| null |  |
| `summary.min_fee_rate` | number \| null | A replacement: the lowest fee rate that replaces the pending transaction. |
| `summary.layout` | array of object | How each source was cut (layout kinds). |
| `summary.layout[].outpoint` | string |  |
| `summary.layout[].input` | integer |  |
| `summary.layout[].separated` | boolean | Something was split off. |
| `summary.layout[].borrow` | integer | Sats the last output borrowed from the funding. |
| `summary.layout[].merged` | array of object | Picked sats sharing one output (too close together, or at the tail). |
| `summary.layout[].rides_along` | integer |  |
| `summary.layout[].chunks` | integer | A split: the chunks planned for this source. |
| `summary.layout[].chunk_outputs` | integer | A split: the outputs they make (fewer when chunks sit too close together to separate; a `merged` warning). |
| `summary.split` | object | An extract with `split`: the split as planned (the request's, normalized). |
| `summary.split.type` | string |  |
| `summary.split.per_output` | integer \| null |  |
| `summary.split.chunks` | integer \| null |  |
| `summary.split.trim` | boolean |  |
| `expires_at` | integer | Unix seconds: submit before then (10 minutes). |

```json title="Response"
{
  "id": "6baec47455bf8df8",
  "psbt": "cHNidP8BAP0dAQIAAAACfxBUfmD8FgvLWC+rhhI7HaW1ncmmlqpbSg8h3N/usyQAAAAAAP3///9AKU7jykAIeJ8gRh6iFq7aPfAmRiqhMPIbX7W1YPtYHgAAAAAA/f///wWEAQAAAAAAACJRIKYIafDbzx3GWcnOy6+AUBNeqejNxIcFPx3GiAlJ3GhMSgEAAAAAAAAiUSCmCGnw288dxlnJzsuvgFATXqnozcSHBT8dxogJSdxoTEIkAAAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuH6AMAAAAAAAAiUSCt8pxdM5v1iajbtpINGw6mXK08CmhXiuI0TjokZbsX8J1ZAAAAAAAAF6kUP7bpWBLle7RpH5pKYohiphpPdpuHAAAAAAABASsQJwAAAAAAACJRIKYIafDbzx3GWcnOy6+AUBNeqejNxIcFPx3GiAlJ3GhMARcgzIpLxk2Je93F+8L2cPeougs4Z3kQbPEiPG/F181vwRUAAQEgqGEAAAAAAAAXqRQ/tulYEuV7tGkfmkpiiGKmGk92m4cBBBYAFPmQZ5rK/iXCdhU3O0C/IkRtJP9EAAAAAAAA",
  "sign": [
    {
      "index": 0,
      "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr"
    },
    {"index": 1, "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"}
  ],
  "summary": {
    "kind": "extract",
    "network_fee": 1059,
    "tools_fee": 1000,
    "fee_rate": 3,
    "vsize": 353,
    "change": 22941,
    "total_cost": 2059,
    "leaves_wallet": 0,
    "destination": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
    "external": false,
    "inputs": [
      {
        "index": 0,
        "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
        "value": 10000,
        "role": "source",
        "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "owner_role": "ordinals",
        "items": [
          {
            "sat": 441000000000000,
            "len": 1,
            "offset": 388,
            "types": ["uncommon", "alpha", "decasat_alpha"],
            "selected": true
          }
        ],
        "inscriptions": 0,
        "runes": false
      },
      {
        "index": 1,
        "outpoint": "1e58fb60b5b55f1bf230a12a4626f03ddaae16a21e46209f780840cae34e2940:0",
        "value": 25000,
        "role": "funding",
        "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "owner_role": "payment",
        "items": [],
        "inscriptions": 0,
        "runes": false
      }
    ],
    "outputs": [
      {
        "index": 0,
        "value": 388,
        "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "role": "return",
        "to": "source",
        "parts": [{"input": 0, "from": 0, "to": 388, "at": 0}],
        "items": [],
        "rides_along": [],
        "padding": 0,
        "extracted": null
      },
      {
        "index": 1,
        "value": 330,
        "address": "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
        "role": "dest",
        "to": "ordinals",
        "parts": [{"input": 0, "from": 388, "to": 718, "at": 0}],
        "items": [
          {
            "sat": 441000000000000,
            "len": 1,
            "types": ["uncommon", "alpha", "decasat_alpha"],
            "input": 0,
            "offset": 388,
            "at": 0
          }
        ],
        "rides_along": [],
        "padding": 329,
        "extracted": true
      },
      {
        "index": 2,
        "value": 9282,
        "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "role": "plain_return",
        "to": "payment",
        "parts": [
          {"input": 0, "from": 718, "to": 10000, "at": 0}
        ],
        "items": [],
        "rides_along": [],
        "padding": 0,
        "extracted": null
      },
      {
        "index": 3,
        "value": 1000,
        "address": "bc1p4hefchfnn06cn2xmk6fq6xcw5ew260q2dptc4c35fcazgedmzlcqkxf057",
        "role": "tools_fee",
        "to": "fee",
        "parts": [{"input": 1, "from": 0, "to": 1000, "at": 0}],
        "items": [],
        "rides_along": [],
        "padding": 0,
        "extracted": null
      },
      {
        "index": 4,
        "value": 22941,
        "address": "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf",
        "role": "change",
        "to": "payment",
        "parts": [
          {"input": 1, "from": 1000, "to": 23941, "at": 0}
        ],
        "items": [],
        "rides_along": [],
        "padding": 0,
        "extracted": null
      }
    ],
    "checks": [
      {
        "rule": "fifo",
        "ok": true,
        "label": "The fee is paid only from your plain bitcoin"
      },
      {"rule": "fifo", "ok": true, "label": "No rare sat is paid as fee"},
      {
        "rule": "fifo",
        "ok": true,
        "label": "Every rare sat lands in its planned output"
      },
      {
        "rule": "fifo",
        "ok": true,
        "label": "No inscription or rune leaves its UTXO"
      },
      {"rule": "owner_inputs", "ok": true, "label": "Every input is yours"},
      {"rule": "no_op_return", "ok": true, "label": "No OP_RETURN output"},
      {
        "rule": "dust",
        "ok": true,
        "label": "Every output is above the dust limit"
      },
      {
        "rule": "tools_fee",
        "ok": true,
        "label": "One flat raresats.net fee output"
      },
      {
        "rule": "change",
        "ok": true,
        "label": "Your change returns to your payment address"
      },
      {
        "rule": "network_fee",
        "ok": true,
        "label": "The network fee matches the fee rate"
      },
      {
        "rule": "standard",
        "ok": true,
        "label": "A standard, replaceable transaction"
      }
    ],
    "warnings": [],
    "skipped": [],
    "replaces": null,
    "min_fee_rate": null,
    "layout": [
      {
        "outpoint": "24b3eedfdc210f4a5baa96a6c99db5a51d3b1286ab2f58cb0b16fc607e54107f:0",
        "input": 0,
        "separated": true,
        "borrow": 0,
        "merged": [],
        "rides_along": 0
      }
    ]
  },
  "expires_at": 1791428836
}
```

#### Errors

`{error, code}`; see [Errors](https://raresats.net/docs/errors.md) for every code.

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `bad_address` | An address is not a mainnet address (or not one at all). |
| 400 | `bad_chop_size` | `chop_size` is not a whole number from 330 to 100,000,000. |
| 400 | `bad_count` | `count` (send N) is not a whole number of sats of at least 1. |
| 400 | `bad_fee_rate` | `fee_rate` is not a positive number up to 5000. |
| 400 | `bad_items` | `items` is not a list of 1-50 UTXOs (or the PSBT lists more), or an item's `sats` is not null or a list of sat numbers. |
| 400 | `bad_json` | The body is not valid JSON. |
| 400 | `bad_kind` | `kind` is not one of the wallet tools (`extract`, `send`, `send_n`, `chop`, `consolidate`, `multisend`, `speedup`, `cancel`), or a send `mode` is unknown. |
| 400 | `bad_outpoint` | Not an outpoint (`txid:vout`). |
| 400 | `bad_owner` | `owner` must give the payment and ordinals addresses and their public keys. |
| 400 | `bad_request` | The body is not a JSON object, or the engine refused a malformed request (e.g. a purchase submitted without its signed setup PSBT). |
| 400 | `bad_sends` | `sends` must list 1-50 payments of `{address, value}`, each at least its address type's minimum (330, or 546 for P2SH and P2PKH). |
| 400 | `bad_split` | `split` (extract) is not `{type, per_output \| chunks, trim?}`: an exact type combination that is not a whole range, `per_output` 1–2,000 or `chunks` 2–100 (exactly one), `trim` a boolean; or an item names `sats` (a split takes every sat of its type). |
| 400 | `bad_type` | `type` (send N) is not a sat type, types joined with `+`, or `block9` / `450x`. |
| 400 | `duplicate_outpoint` | The same UTXO twice in one listing. |
| 403 | `cross_origin` | A POST from a browser on another site (a foreign `Origin`, or `sec-fetch-site: cross-site`). Server-side clients send no `Origin` and are not affected. |
| 404 | `unknown_transfer` | No such transfer: an unknown or forgotten prepared id, or a txid the wallet tools did not build. |
| 405 | `method_not_allowed` | Wrong method for the route; the `allow` header lists the right ones. |
| 409 | `conflict` | A listing or one of your UTXOs was spent meanwhile (lost race), or bitcoind would not replace a transaction already spending it (`insufficient fee`), or a transfer's inputs are spent by another transaction. |
| 409 | `has_descendants` | A transaction spends the transfer's outputs already: it can no longer be replaced. |
| 409 | `not_pending` | The transfer to speed up or cancel is not waiting in the mempool any more (confirmed, replaced). |
| 409 | `nothing_to_cancel` | A cancel that could save nothing: the transfer was refused (never sent), or it (and, once dropped, every transfer it replaced) only pays your own addresses. |
| 409 | `pending` | The UTXO's listing is being bought, or an offer of it accepted, right now. |
| 409 | `spent` | The UTXO (or a listing's UTXO) is spent or does not exist. |
| 409 | `unconfirmed` | The UTXO is not confirmed yet. |
| 409 | `unindexed` | The ord indexer does not know the UTXO yet. |
| 413 | `too_large` | The body is over 256 KiB. |
| 415 | `not_json` | A POST without `content-type: application/json`. |
| 422 | `address_too_large` | An owner address holds more than 5,000 outputs: the wallet tools cannot plan for it. |
| 422 | `already_extracted` | The UTXO is extracted already (its rare sats sit in an output of their own). |
| 422 | `bad_destination` | The destination (or a multisend recipient) is the marketplace's fee address, or missing. |
| 422 | `dust_destination` | A whole UTXO is too small for the destination's address type. |
| 422 | `fee_rate_too_low` | `fee_rate` is below the current minimum (`GET /api/market/fees`). |
| 422 | `fee_too_high` | The network fee would exceed the per-transaction cap (200,000 sats): lower the fee rate or move fewer. |
| 422 | `inscribed` | The UTXO carries an inscription: only rare sats are listed; the wallet tools only send it whole. |
| 422 | `inscribed_consolidate` | Inscribed UTXOs are sent whole and 1:1, never merged into one output. |
| 422 | `insufficient_funds` | The payment address's plain UTXOs cannot pay prices, fee, padding and network fee (or a transfer's fees). Carries `available`, `prices`, `platform_fee`. |
| 422 | `keep_in_destination` | Exotic or unpicked tracked sats sit so close to a picked one that they would have to leave with it: send the whole UTXO instead. |
| 422 | `key_mismatch` | The public key does not control the address. |
| 422 | `legacy_address` | A legacy (P2PKH, `1…`) address: use Taproot or SegWit. |
| 422 | `listed_public_alone` | A UTXO whose listing signature is public must be moved on its own (and fast). |
| 422 | `not_enough_sats` | Your UTXOs do not hold `count` sats of that type that can be sent. Carries `available`. |
| 422 | `not_owner` | The UTXO does not belong to the seller address, or (wallet tools) to the owner's addresses, or the transfer was made by another wallet. |
| 422 | `not_plain` | A UTXO to consolidate is not plain bitcoin (it holds tracked, exotic or inscribed sats, or runes). |
| 422 | `nothing_to_do` | Nothing to move: every item was skipped (`skipped`), or a consolidation has fewer than 2 UTXOs, or a chop leaves one piece. |
| 422 | `nothing_to_separate` | Extracting would leave the UTXO one output: its rare sats are apart already, or too close to its ends. |
| 422 | `payment_destination` | Rare sats, inscriptions or runes would go to your own payment address, which your wallet spends as plain bitcoin: send them to your ordinals address, or say `confirm_payment_destination: true`. |
| 422 | `range_too_small` | A range chunk (send N of `block9` or `450x`) must be at least the destination's padding: 330 sats, 546 for P2SH and P2PKH. |
| 422 | `replacement_fee_too_low` | The replacement pays too little to replace the pending transaction (BIP 125): raise the fee rate to at least `min_fee_rate`. |
| 422 | `runes` | The UTXO carries runes (the wallet tools only send it whole). |
| 422 | `runes_multi` | At most one rune UTXO per transfer (except a whole send merged into one output). |
| 422 | `split_chunk` | Skip code of an "extract all" over several UTXOs: the UTXO is a chunk a split on this site made (extracting it again would undo the split). Name it alone to extract it. |
| 422 | `too_many_items` | Too many UTXOs in a listing, or listings in a purchase (`max_bulk`); for a wallet transfer, too many rare sats (and the runs between them) to lay out in one UTXO: choose fewer. |
| 422 | `too_many_outputs` | The transfer would have more than 100 outputs: move fewer UTXOs at once. |
| 422 | `tx_too_large` | The transfer would exceed 100,000 vB: move fewer UTXOs at once. |
| 422 | `unsupported_address` | An address type the marketplace does not trade (only Taproot, native SegWit and nested SegWit). |
| 422 | `untracked` | The UTXO holds no rare sat the index tracks (send it whole instead). |
| 429 | `rate_limited` | Over 30 POSTs a minute from your IPv4 address or IPv6 /64 (in chat, counted per IPv4 address or IPv6 /64: 30 writes and 20 login nonces a minute, 20 sign-ins and 10 display-name changes an hour; one message every 3 s and 20 a minute per address); wait `retry-after` seconds. With the AI moderation on, at most 6 messages and names a minute are checked per address and per IPv4 address or IPv6 /48. |
| 500 | `invariant_violated` | A planned or finalized purchase or transfer broke an invariant (`violations`); nothing was broadcast. |
| 503 | `bitcoind_unavailable` | bitcoind is not available. |
| 503 | `busy` | Too many purchases, offers or transfers are being prepared: try again in a minute. Each client (IPv4 address or IPv6 /64) keeps its 10 latest prepared purchases, offers and transfers; an older one answers `unknown_purchase` / `unknown_offer` / `unknown_transfer`. |
| 503 | `engine_unavailable` | The trade engine is not available. |
| 503 | `fork_behind` | The rare-sat index has not caught up with bitcoind: try again in a minute. |
| 503 | `fork_unavailable` | The rare-sat index is not available. |
| 503 | `indexer_behind` | The ord indexer is catching up: try again shortly. |
| 503 | `market_unavailable` | The marketplace is closed right now (its backends disagree on the network, or are unreachable). |
| 503 | `oracle_unavailable` | The ord oracle is not available. |

```json title="Error (422)"
{
  "error": "c3f31009cbbbced4b1a0acb30d924e3e025b10647c43092b68262f4e83125bf6:0 would stay one output: its rare sats are already apart, or too close to its ends",
  "code": "nothing_to_separate"
}
```

Then sign **every** input in `sign` (each with the key of its `address`; your wallet's default sighash) and [submit](https://raresats.net/docs/submit-transfer.md) before `expires_at`. Nothing is reserved meanwhile: if a UTXO moves, submit answers 409 and you prepare again.

## Kinds

| `kind` | Body fields | What the transaction does |
| --- | --- | --- |
| `extract` | `items: [{outpoint, sats?}]`, `destination?`, `plain_to?`, `split?` | Carves the chosen tracked sats (all of them when `sats` is absent) of 1–50 UTXOs into outputs of their own, to `destination` (default: your ordinals address). What you did not pick goes back to the address it came from. With `split: {type, per_output \| chunks, trim?}` it cuts each UTXO's sats of one type into chunks instead, one output per chunk ([Split into chunks](https://raresats.net/docs/guide-extract.md#split-into-chunks)). |
| `send` | `items`, `destination`, `mode: "whole"\|"rare"`, `consolidate?` | `whole`: the UTXOs as they are, one output each (or one output for all with `consolidate: true`). `rare`: only the rare sats, laid out as an extraction; the rest comes back. |
| `send_n` | `type`, `count`, `destination`, `items?` | N sats of one type: an exact type combination such as `uncommon` or `black+uncommon`, or a range type (`block9`, `450x`) cut as an aligned sub-range. |
| `chop` | `items`, `chop_size`, `destination?` | Cuts a UTXO's whole ranges (block 9 style) into `chop_size`-aligned pieces. |
| `consolidate` | `items?`, `destination?` | Merges plain UTXOs into one output (default: your payment address). Without `items` the server picks your payment address's plain UTXOs: up to 50, smallest first, never a listed UTXO or your offers' funding, and not the largest when it is worth more than all the others together (merging into it would only cost fees). Fewer than 2 left is `nothing_to_do`. |
| `multisend` | `sends: [{address, value}]` | Plain bitcoin to 1–50 recipients, change back to you. |
| `speedup` | `txid` | Replaces a pending transfer the site built with the same transaction at a higher fee rate (RBF). |
| `cancel` | `txid` | Replaces (or voids) a transfer the site built with one that sends every source UTXO back to where it came from. |

Every kind also takes `owner` and `fee_rate`. The guides walk through them: [Extract rare sats](https://raresats.net/docs/guide-extract.md), [Send rare sats and bitcoin](https://raresats.net/docs/guide-send.md), [Speed up or cancel](https://raresats.net/docs/guide-speed-up.md).

## The summary

`summary` is the plan drawn for people: each input with its tracked sats (`items`, `selected`), each output with what it holds (`items`, `rides_along`, `padding`) and where it goes (`to`: `ordinals`, `payment`, `source`, `external` or `fee`), the `checks` the server ran on the unsigned transaction, and `warnings` and `skipped` items with their reasons (for example `listed`, `offered`, `inscribed`, `runes`, `already_extracted`, `nothing_to_separate`, or `spent` for a UTXO `send_n` would have picked that is already spent in the mempool). An output that carries home sats that are not plain (unpicked rare sats, exotic ranges such as vintage) lists them in `kept`, with their types. A split also answers `split` (as planned) and, per source in `layout`, `chunks` and `chunk_outputs`. `total_cost` is the network fee plus the tools fee; `leaves_wallet` is what is paid to addresses that are not yours (other than the fees).
