IBExWalletAPI
Chains

Bitcoin Integration

IBEX FI API – Bitcoin Endpoints (Client Integration Guide)

This document explains how to integrate all Bitcoin-related endpoints exposed by IBEX FI API. It covers two complementary flows:

  • Direct, off-chain Bitcoin helpers under GET/POST /v1.2/safes/bitcoin/*
  • An authorization wrapper via Safe Operations using the BITCOIN_SEND operation type on POST /v1.2/safes/operations (no on-chain Safe tx is created; it is an off-chain flow for client-side PSBT signing and broadcast)

Unless stated otherwise, endpoints require JWT auth via an Authorization: Bearer <token>`` header. All amounts are expressed in satoshis where applicable.

How fees are paid (two operating modes)

  • Simple mode (wallet pays own fees):
    • The sender wallet A funds the transaction and the network fees from A's UTXOs.
    • Use /v1.2/safes/bitcoin/send/prepare (or BITCOIN_SEND via /v1.2/safes/operations) to get prepared.
    • A builds and signs the PSBT locally and broadcasts via /v1.2/safes/bitcoin/tx/broadcast.
    • Effect: fees are implicitly paid by A (sum(inputs A) − sum(outputs) = feeSat).
  • Collaborative PSBT (sponsored by another wallet):
    • A defines the business outputs (e.g., amount to C) and exports an unsigned PSBT.
    • B adds its own inputs (and B's change) to cover the fee, signs only B's inputs, and returns the PSBT to A.
    • A verifies outputs are unchanged, signs only A's inputs, finalizes, and broadcasts.
    • Effect: fees are paid from B's inputs without exposing A or B private keys to the server; use SIGHASH_ALL and let A sign last.

Networks

  • Use query/body param network with values mainnet or testnet where supported.
  • If omitted, the server infers a default from environment (e.g., BTC_NETWORK).

1) Bitcoin helper endpoints (/v1.2/safes/bitcoin)

These endpoints help you fetch UTXOs, estimate fees, prepare simple sends, and broadcast a signed raw transaction. No server-side signing occurs.

GET /v1.2/safes/bitcoin/info

  • Auth: JWT
  • Purpose: Basic network info (proxy to Core getblockchaininfo).
  • Query params:
    • network (optional): mainnet | testnet
  • 200 Response (example):
{
  "network": "testnet",
  "rpc": { /* getblockchaininfo result */ }
}

Example curl:

curl -s \
  -H "Authorization: Bearer $JWT" \
  "https://api.example.com/v1.2/safes/bitcoin/info?network=testnet"

GET /v1.2/safes/bitcoin/fees

  • Auth: JWT
  • Purpose: Estimate fee tiers using Core estimatesmartfee for 1/6/12 blocks.
  • Query params:
    • network (optional): mainnet | testnet
  • 200 Response:
{ "feeRateSatVb": { "fast": 22, "standard": 12, "slow": 8 } }

Example curl:

curl -s -H "Authorization: Bearer $JWT" \
  "https://api.example.com/v1.2/safes/bitcoin/fees"

GET /v1.2/safes/bitcoin/utxos

  • Auth: JWT
  • Purpose: List UTXOs for a Bitcoin address (uses Core scantxoutset addr(<address>)).
  • Query params:
    • address (required): Bitcoin address (bech32 recommended)
    • network (optional): mainnet | testnet
  • 200 Response:
{
  "address": "tb1q...",
  "network": "testnet",
  "utxos": [
    { "txid": "...", "vout": 1, "value": 12345, "scriptPubKey": "0014..." }
  ]
}
  • Notes:
    • value is in satoshis.
    • If utxos is empty, the server returns 200 with an empty list; some composed flows may enforce non-empty.

Example curl:

curl -s -H "Authorization: Bearer $JWT" \
  "https://api.example.com/v1.2/safes/bitcoin/utxos?address=tb1q..."

POST /v1.2/safes/bitcoin/send/prepare

  • Auth: JWT
  • Purpose: Prepare a simple send by selecting UTXOs, estimating fees, computing change. Returns a structure suitable for building a PSBT client-side.
  • Body:
{
  "from": "tb1q...",
  "to": "tb1q...",
  "amountSat": 10000,        // required unless sendAll=true
  "sendAll": false,          // if true, ignores amount and sends all minus fee
  "feeProfile": "standard", // one of slow|standard|fast
  "network": "testnet"
}
  • 200 Response:
{
  "from": "tb1q...",
  "to": "tb1q...",
  "amountSat": 10000,
  "feeSat": 1234,
  "inputsUsed": [
    { "txid": "...", "vout": 0, "value": 22345, "scriptPubKey": "0014..." }
  ],
  "outputs": [ { "address": "tb1q...", "value": 10000 } ],
  "change": { "address": "tb1q...", "value": 11111 }
}
  • Notes:
    • Greedy coin selection; rough vbytes sizing: 68 * nInputs + 31 * nOutputs + 10.
    • If sendAll=true, the amountSat in the response is set to the computed value (sum(UTXO) - feeSat) and there is typically no change output.
    • This endpoint does not build/sign a PSBT; build it client-side using the returned inputsUsed/outputs/change.

Example curl:

curl -s -X POST \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
        "from":"tb1qFROM...",
        "to":"tb1qTO...",
        "amountSat":10000,
        "feeProfile":"standard",
        "network":"testnet"
      }' \
  "https://api.example.com/v1.2/safes/bitcoin/send/prepare"

POST /v1.2/safes/bitcoin/tx/broadcast

  • Auth: JWT
  • Purpose: Broadcast a signed raw transaction (hex) to the network.
  • Body:
{ "rawtx": "020000000001...", "network": "testnet" }
  • 200 Response:
{ "txid": "<transaction-id>" }

Example curl:

curl -s -X POST \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"rawtx":"020000000001...","network":"testnet"}' \
  "https://api.example.com/v1.2/safes/bitcoin/tx/broadcast"

POST /v1.2/safes/bitcoin/psbt/build (placeholder)

  • Auth: JWT
  • Purpose: Reserved for a future server-side PSBT builder. Currently returns 501 Not Implemented.

2) Safe Operations wrapper for Bitcoin – BITCOIN_SEND

You can request a WebAuthn authorization and receive the same prepared structure via the Safe Operations endpoint by submitting only BITCOIN_SEND operations. No Safe (EVM) transaction is created; the flow remains off-chain for Bitcoin.

POST /v1.2/safes/operations

  • Auth: JWT
  • Body (example):
{
  "safeAddress": "0xSafe...",      
  "operations": [
    {
      "type": "BITCOIN_SEND",
      "network": "testnet",
      "from": "tb1qFROM...",
      "to": "tb1qTO...",
      "amountSat": 10000,
      "sendAll": false,
      "feeProfile": "standard"
    }
  ]
}
  • 200 Response:
{
  "credentialRequestOptions": { /* WebAuthn options for user authorization */ },
  "prepared": {
    "from": "tb1q...",
    "to": "tb1q...",
    "amountSat": 10000,
    "feeSat": 1234,
    "inputsUsed": [ { "txid": "...", "vout": 0, "value": 22345, "scriptPubKey": "0014..." } ],
    "outputs": [ { "address": "tb1q...", "value": 10000 } ],
    "change": { "address": "tb1q...", "value": 11111 },
    "network": "testnet"
  }
}
  • Notes:
    • This route performs the same preparation as /send/prepare, adds a WebAuthn credentialRequestOptions challenge, and returns immediately.
    • No Safe user operation is created; do not call PUT /v1.2/safes/operations for this Bitcoin flow.
    • Use the prepared object to build/sign your PSBT client-side, then broadcast via /v1.2/safes/bitcoin/tx/broadcast.

3) Building and signing a PSBT (client-side)

Use the output of /send/prepare or the prepared object from BITCOIN_SEND to construct a PSBT:

  • Inputs: for each inputsUsed[i], add a PSBT input with txid, vout, and the script/amount context your library requires (e.g., nonWitnessUtxo or witnessUtxo derived from scriptPubKey and value).
  • Outputs: add the main to output and the optional change output if present.
  • Fees: the server returns an estimated feeSat; total inputs minus total outputs equals feeSat.
  • Sign: sign with the private key controlling from (client custody).
  • Finalize and extract hex.
  • Broadcast the hex with POST /v1.2/safes/bitcoin/tx/broadcast.

Because wallet libraries differ, refer to your PSBT library docs (e.g., bitcoinjs-lib, bc-lib, etc.) for the exact fields to populate per input type (P2WPKH, P2TR, etc.).


4) Typical integration sequence

  1. Fetch UTXOs for the sender address: GET /v1.2/safes/bitcoin/utxos?address=...
  2. Get fee tiers: GET /v1.2/safes/bitcoin/fees
  3. Prepare send: POST /v1.2/safes/bitcoin/send/prepare (or POST /v1.2/safes/operations with a single BITCOIN_SEND op if you need WebAuthn authorization)
  4. Build PSBT client-side using the prepared structure
  5. Sign PSBT client-side
  6. Broadcast: POST /v1.2/safes/bitcoin/tx/broadcast

5) Validation and error handling

  • 400 Bad Request
    • Missing/invalid fields (e.g., amountSat must be > 0 when sendAll=false)
    • No UTXOs for from address
    • Insufficient funds (including fees)
    • For BITCOIN_SEND via /v1.2/safes/operations: 'from' is required when using that path
  • 401 Unauthorized
    • Missing/invalid JWT
  • 409 Conflict
    • Not applicable to Bitcoin helpers; may appear in other Safe flows
  • 500 Internal Server Error
    • Underlying RPC failure (info/fees/utxos), prepare failure, broadcast errors
  • 501 Not Implemented
    • /v1.2/safes/bitcoin/psbt/build

6) Notes and constraints

  • The server does not sign Bitcoin transactions. Keep custody and signing on the client.
  • Coin selection is greedy and fee estimation is approximate; you may re-run prepare with a different feeProfile.
  • Address validation is minimal server-side; ensure your client validates address formats.
  • For sendAll=true, the change is omitted and the amountSat is adjusted to use all inputs minus fees.

7) Quick examples

Prepare (send specific amount):

curl -s -X POST \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
        "from":"tb1qFROM...",
        "to":"tb1qTO...",
        "amountSat":25000,
        "feeProfile":"fast",
        "network":"testnet"
      }' \
  "https://api.example.com/v1.2/safes/bitcoin/send/prepare"

Broadcast:

curl -s -X POST \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"rawtx":"020000000001...","network":"testnet"}' \
  "https://api.example.com/v1.2/safes/bitcoin/tx/broadcast"

Safe Operations wrapper (request WebAuthn + prepare):

curl -s -X POST \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
        "safeAddress":"0xSafe...",
        "operations":[{
          "type":"BITCOIN_SEND",
          "network":"testnet",
          "from":"tb1qFROM...",
          "to":"tb1qTO...",
          "amountSat":10000,
          "feeProfile":"standard"
        }]
      }' \
  "https://api.example.com/v1.2/safes/operations"

Use the returned prepared object to build and sign your PSBT client-side, then broadcast.