Build a transaction

Build an unsigned transaction (UTXO, ETH or TRON, including token transfers) and get the digests to sign.

# Build a transaction

`POST https://api.rarebl.com/open/tx/build`

Builds an unsigned transaction on any chain and returns the digests to sign. The API supplies UTXOs or nonce and fees; your keys never leave you.

**Rate class:** compute (2 requests per second, burst 10, per IP). **Available since:** Open API v2.

## Request

Send a JSON body (`Content-Type: application/json`). No API key is needed: `X-API-KEY` is reserved and ignored.

Example: `curl -X POST "https://api.rarebl.com/open/tx/build" -H "Content-Type: application/json" -d '{"chain":"dash","from":"[\"XnL1FvTKqe6aWfB8NrvgbUMuWCEA3dNxfF\"]","to":"[{\"address\":\"XiM9Afap6UEb5LDkcWrg2YyA28hifkZZKM\",\"amount\":\"0.01\"}]","priority":"standard"}'`

## Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `chain` | string | Yes | The chain to build for. One of: btc, ltc, bch, dash, eth, tron. |
| `from` | array \| string | No | UTXO chains: an array of addresses we may spend from (watched on BTC/LTC/BCH, any on DASH). ETH and TRON: the sender address (one address). Use `from` or `inputs` on UTXO chains. |
| `inputs` | array | No | UTXO chains: exact inputs `[{"txid","vout"}]`, verified unspent with `gettxout`. Any UTXO chain. At most 10 inputs on the hosted endpoints. |
| `to` | array \| string | Yes | UTXO chains: recipients `[{"address","amount"}]` (one entry only). ETH and TRON: the recipient address (one recipient). `amount: "max"` sends everything less the fee. |
| `amount` | string | No | ETH and TRON: the amount in whole units (or `"max"`), as the SDKs send it. UTXO chains put the amount inside each `to` entry. |
| `change` | string | No | UTXO change address. Default: the first `from` address. |
| `feeRate` | string | No | Fee rate in sat/vB (UTXO). Use `feeRate` or `priority`. |
| `priority` | string | No | Fee choice instead of `feeRate`. One of: slow, standard, fast. |
| `rbf` | boolean | No | Signal replace-by-fee (BTC and LTC). Default true. One of: true, false. |
| `opReturn` | string | No | Optional OP_RETURN payload as hex. At most 80 bytes; UTXO chains. |
| `token` | string | No | ERC-20 (ETH) or TRC-20 (TRON) contract address for a token transfer. |
| `gasLimit` | string | No | ETH: gas limit. |
| `maxFeePerGas` | string | No | ETH: max fee per gas, Gwei. |
| `maxPriorityFeePerGas` | string | No | ETH: max priority fee per gas, Gwei. |
| `feeLimit` | integer | No | TRON: fee limit in sun. |

## Responses

### API200 (HTTP 200): OK

A Dash transaction built from one input: a real reply (nothing was signed or broadcast).

```json
{
  "status": 1,
  "code": "API200",
  "message": "success",
  "data": {
    "unsigned": {
      "chain": "dash",
      "format": "utxo-v1",
      "tx": "0200000001139f6185036ea872d9e52bd7424ba9165f31b6479556b021fe4b8f3f42e8087a0100000000ffffffff0240420f00000000001976a91454072845a9ca039a5b585f5fd994d88d38feb31c88ac189f3419000000001976a9147fb0a45f00c25ff1cb67753221326c7c420b8b2388ac00000000",
      "inputs": [
        {
          "txid": "7a08e8423f8f4bfe21b0569547b6315f16a94b42d72be5d972a86e0385619f13",
          "vout": 1,
          "address": "XnL1FvTKqe6aWfB8NrvgbUMuWCEA3dNxfF",
          "value": "4.23879226",
          "valueSats": "423879226",
          "scriptPubKey": "76a9147fb0a45f00c25ff1cb67753221326c7c420b8b2388ac",
          "scriptType": "p2pkh",
          "sequence": 4294967295
        }
      ],
      "outputs": [
        {
          "address": "XiM9Afap6UEb5LDkcWrg2YyA28hifkZZKM",
          "value": "0.01",
          "valueSats": "1000000",
          "change": false
        },
        {
          "address": "XnL1FvTKqe6aWfB8NrvgbUMuWCEA3dNxfF",
          "value": "4.22879",
          "valueSats": "422879000",
          "change": true
        }
      ],
      "fee": "0.00000226",
      "feeSats": "226",
      "vsize": 226,
      "feeRate": "1"
    },
    "toSign": [
      {
        "index": 0,
        "address": "XnL1FvTKqe6aWfB8NrvgbUMuWCEA3dNxfF",
        "digest": "60f09e680a8d4d7442ec09e2e89181c568f596c6200c6a2d2f4aa15a4a8fc6b1",
        "algorithm": "ecdsa-secp256k1",
        "sighashType": 1,
        "scriptType": "p2pkh"
      }
    ],
    "expiresAt": "2026-11-15T00:31:27Z"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

| Field | Type | Description |
| --- | --- | --- |
| `data.unsigned` | object | The unsigned transaction. `format` is `utxo-v1`, `eth-1559-v1` or `tron-v1`. UTXO: `tx`, `psbt` (BTC/LTC), `inputs[]` (each legacy p2pkh input on BTC, LTC and DASH also carries `prevTx`, the raw previous transaction as hex), `outputs[]`, `fee`, `feeSats`, `vsize`, `feeRate`. |
| `data.toSign[]` | object[] | `{index, address, digest, algorithm, sighashType?, scriptType?}`: the 32-byte hex digests to sign. |
| `data.expiresAt` | string | ISO 8601. TRON expiration; for UTXO and ETH advisory (fees, nonce). |

### API200 (HTTP 200): OK (ETH token)

An ERC-20 (USDT) transfer on Ethereum (sample values): `unsigned.tx` carries the EIP-1559 fields, the digest is the keccak256 of the typed payload.

```json
{
  "status": 1,
  "code": "API200",
  "message": "success",
  "data": {
    "unsigned": {
      "chain": "eth",
      "format": "eth-1559-v1",
      "tx": {
        "from": "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe",
        "to": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "nonce": 42,
        "gasLimit": "65000",
        "maxFeePerGas": "18.5",
        "maxPriorityFeePerGas": "1.2",
        "value": "0",
        "data": "0xa9059cbb000000000000000000000000dead00000000000000000000000000000000dead00000000000000000000000000000000000000000000000000000000017d7840",
        "chainId": 1
      }
    },
    "toSign": [
      {
        "index": 0,
        "address": "0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe",
        "digest": "9b2c4f0a6d81e3375a40c1be52d9f8a7140c6e3bd52a97f08e1c4d63b0a5f287",
        "algorithm": "ecdsa-secp256k1-recoverable"
      }
    ],
    "expiresAt": "2026-11-15T00:50:00Z"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API400 (HTTP 400): Bad Request

A parameter is missing or malformed. `data.param` names it (here `chain`).

```json
{
  "status": 0,
  "code": "API400",
  "message": "Unknown or missing chain.",
  "data": {
    "param": "chain"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API409 (HTTP 409): Conflict

BTC, LTC or BCH: a `from` address is not watched and active yet. Start a watch with [address/watch](https://docs.rarebl.com/api/address/watch).

```json
{
  "status": 0,
  "code": "API409",
  "message": "This address is not watched yet.",
  "data": {
    "reason": "not_watched"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API422 (HTTP 422): Unprocessable Entity

The selected inputs cannot pay the outputs plus the fee.

```json
{
  "status": 0,
  "code": "API422",
  "message": "The inputs do not cover the amount and the fee.",
  "data": {
    "reason": "insufficient_funds"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API422 (HTTP 422): Unprocessable Entity

An output (often the change) is too small to be relayed.

```json
{
  "status": 0,
  "code": "API422",
  "message": "An output is below the dust limit.",
  "data": {
    "reason": "dust_output"
  },
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API429 (HTTP 429): Too Many Requests

Rate limited (compute class: 2 requests per second, burst 10, per IP). A `Retry-After` header says how long to wait, in seconds.

```json
{
  "status": 0,
  "code": "API429",
  "message": "Too many requests. Please retry shortly.",
  "data": null,
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

### API502 (HTTP 502): Bad Gateway

The node is unavailable, still syncing or timed out. Retry later with a short back-off.

```json
{
  "status": 0,
  "code": "API502",
  "message": "The node is unavailable or timed out. Please try again.",
  "data": null,
  "go": null,
  "meta": {
    "requestid": "0123456789abcdef",
    "notices": []
  }
}
```

## Notes

- Three ways to send: [sign locally with the SDK](https://docs.rarebl.com/guides/sign-locally) (recommended), [sign the digests yourself](https://docs.rarebl.com/guides/send-a-transaction), or [hosted signing](https://docs.rarebl.com/guides/hosted-signing). See [Send a transaction](https://docs.rarebl.com/guides/send-a-transaction).
- The digest depends on the script type: legacy sighash for p2pkh, BIP143 for p2wpkh and p2sh-p2wpkh, BIP341 for p2tr key path (Schnorr), BIP143-style with FORKID (`0x41`) for BCH, keccak256 of the EIP-1559 payload for ETH and sha256 of `raw_data` for TRON. ECDSA signatures are RFC 6979 and low-S.
- ETH and TRON take `from` (one address), `to` (one recipient) and optionally `token`. ETH also takes `gasLimit`, `maxFeePerGas` and `maxPriorityFeePerGas`; TRON takes `feeLimit` (sun).
- Legacy p2pkh inputs (BTC, LTC, DASH) carry `prevTx` in `unsigned.inputs[]`. A legacy signature does not commit to the input amount, so the SDKs check that `prevTx` hashes to the input's `txid` and read the amount from it before signing.
- The [SDKs](https://docs.rarebl.com/sdks/js) build, verify and sign locally, so you rarely call this endpoint by hand. See [Send a transaction](https://docs.rarebl.com/guides/send-a-transaction).
- The ETH example shows the shape; its hex strings and digest are sample values.

Back to the Rarebl AI documentation index