Transaction Signing & Approval

Transaction Signing Callback Handler

POST /v2/tx_sign_request

This request is used for transaction signing and approval and expects a CallbackResponse object from the callback handler. If the callback handler does not respond within 30 seconds, Fireblocks fails the request.

If both approval and signing are requested for a transaction, one callback handles both requests.

To differentiate whether this request is for signing, approving, or both signing and approving a transaction, these parameters are included only with transaction signing requests:

Request parameters

ParameterTypeDescription
txIdstringThe transaction's internal ID in Fireblocks.
operationstringThe transaction operation type. The default is TRANSFER.
sourceTypestringThe source of the transaction.
sourceIdstringThe transaction’s source vault account ID or exchange account UUID.
destTypestringThe transaction's destination type. The type can be VAULT, EXCHANGE, or UNMANAGED.
destIdstringThe destination's vault account ID or exchange account UUID.
assetstringThe asset ID in Fireblocks.
amount (Deprecated)numberPlease use the amountStr field for accuracy.
(If the transfer is a withdrawal from an exchange, the actual transfer amount. Otherwise, the requested amount.)
amountStrstringThe amount of the transfer in string format.
requestedAmount (Deprecated)numberPlease use the requestedAmountStr field for accuracy. (The requested transfer amount. In gross transactions, transaction fees are deducted from this amount.)
requestedAmountStrstringThe requested transfer amount in string format. In gross transactions, transaction fees are deducted from this amount.
feestring(Optional) The transaction's estimated fee.
destAddressTypestringThe destination's address type. For one-time addresses on EVM-compatible blockchains, this is the smart contract address.
destAddressstringThe destination address of the transaction.
destinationsarrayAn array of TransactionRequestCallbackDestination objects with all details for all destination(s).
playersarray (string)A list of the Co-Signers that signed the transaction. (Two Fireblocks SaaS and at least one user device or one API Co-Signer). Each signer is represented by a Device ID.
requestIdstringA unique identifier of this request; is returned in the response.
signerIdstringThe user that signed the transaction.
extraParametersarray(Optional) Parameters that are specific to some transaction operation types and blockchain networks. Learn more here.
notestring(Optional) Custom note that describes this transaction in your Fireblocks workspace. The note isn’t sent to the blockchain.
rawTxarrayAn array of RawTX. Contains a list of the actual transactions sent to the blockchain. Note that some signing requests represent multiple transactions. When this occurs, the list contains more than one object. Additionally, this parameter is not included in the CallbackResponse object when using a Fireblocks EU cloud environment.
action (deprecated)objectIncludes information about the transaction authorization policy rule that matched this transaction. This field and its contents are not officially supported or maintained. Fireblocks may delete or change the contents of this field at any time.

TransactionOperation

ParameterTypeDescription
operationstring[TRANSFER, MINT, BURN, CONTRACT_CALL, TYPED_MESSAGE, RAW, ENABLE_ASSET, STAKE, UNSTAKE, WITHDRAW]
Not all operations are available in all workspaces. Contact your account manager to enable additional features.

TRANSFER - Default. Transfers funds from one account to another. UTXO blockchains allow multi-input and multi-output transfers. All other blockchains allow transfers with one source address and one destination address.

MINT - Mints new tokens. Supported for Stellar, Ripple, and EVM-based blockchains.

BURN - Burns tokens. Supported for Stellar, Ripple, and EVM-based blockchains.

CONTRACT_CALL - Calls a smart contract method for web3 operations on any EVM blockchain. The Fireblocks development libraries are recommended for building contract call transactions.

TYPED_MESSAGE - An off-chain message in either Ethereum Personal Message or EIP712 format. Use it to sign specific readable messages that are not actual transactions. Learn more about typed messages.

RAW - An off-chain message with no predefined format. Use it to sign any message with your private key, including protocols such as blockchains and custom transaction types that are not natively supported by Fireblocks. Learn more about raw signing transactions.

ENABLE_ASSET - Algorand, DigitalBits, Solana, and Stellar require an on-chain transaction to create an asset wallet and enable the deposit address. This transaction is automatically created when adding assets on these blockchains to a vault account.

STAKE - Assign assets to a staking pool managed by a staking validator. Learn more about staking transactions and supported blockchains. This transaction is automatically created when performing staking operations.

UNSTAKE - Remove assets from a staking pool managed by a staking validator. Learn more about staking transactions and supported blockchains. This transaction is automatically created when performing staking operations.

WITHDRAW - Transfer assets from a dedicated staking vault account to another address. Learn more about staking transactions and supported blockchains. This transaction is automatically created when performing staking operations.
Note: Fireblocks will rename this type from WITHDRAW to a different name soon. There will be at minimum a 7-day notice regarding the new type name.

TransactionRequestCallbackDestination

ParameterTypeDescription
amountNativestringDeprecated. The amount transferred to this destination as a number. Use the amountNativeStr parameter for accurate precision.
amountNativeStrnumberThe amount transferred to this destination represented as a string.
amountUSDnumberThe USD value of the transfer to this destination.
dstAddressTypeWHITELISTED or ONE_TIMEWHITELISTED or ONE_TIME
dstIdstringThe ID of the destination.
dstNamestringThe name of the destination.
dstSubTypestringThe specific exchange, fiat account, or unmanaged wallet.

For exchange accounts: (BINANCE, BINANCEUS, BITFINEX, BITHUMB, BITMEX, BITSO, BITSTAMP, BITTREX, BYBIT, CIRCLE, COINBASEEXCHANGE, COINBASEPRO, COINMETRO, COINSPRO, CRYPTOCOM, DERIBIT, GEMINI, HITBTC, HUOBI, INDEPENDENTRESERVE, KORBIT, KRAKEN, KRAKENINTL, KUCOIN, LIQUID, OKCOIN, OKEX, PAXOS, POLONIEX)

For fiat accounts: (BLINC)

For unmanaged wallets: (INTERNAL, EXTERNAL, or CONTRACT)
dstTypestringThe destination of the transaction (VAULT, EXCHANGE_ACCOUNT, FIAT_ACCOUNT, or UNMANAGED).
displayDstAddressstringThe address of this specific destination
actionstringIncludes information about the Transaction Authorization Policy rule that matched this transaction.

TransactionExtraParameters

ParameterTypeDescription
inputsSelectionInputsSelection objectFor UTXO-based blockchain multi-input selection, use the inputsSelection field with values set to the input selection structure. The inputs can be retrieved using the Retrieve Unspent Inputs endpoint.
rawMessageDataRawMessageData objectFor RAW operations, use the rawMessageData field with the values set to the raw message data structure. Only included with raw signing transactions on Bitcoin and Ethereum.
This is an opt-in feature. Please contact Fireblocks Support to include this feature in your workspace.
contractCallDatastringFor CONTRACT_CALL operations, use the contractCallData field with the value set to the Ethereum smart contract Application Binary Interface (ABI) payload. The Fireblocks development libraries are recommended for building contract call transactions

RawTX

ParameterTypeDescription
rawTxstringHex-encoded details of a transaction sent to the blockchain
keyDerivationPathArray of numbersLocation of the encryption key within the customer’s HD Wallet URL used to sign this transaction.

📘

Checkout API Co-Signer Callback Handler examples:

  1. Basic JS and Python examples
  2. Python Plugin Based Callback Handler boilerplate



API Co-Signer callback code example

const express = require("express");
const bodyParser = require("body-parser");
const fs = require("fs");
const jwt = require("jsonwebtoken");
const privateKey = fs.readFileSync("callback_private.pem");
const cosignerPubKey = fs.readFileSync("cosigner_public.pem");
const app = express();

app.use(
  express.urlencoded({
    extended: true
  })
);
app.use(express.json());

app.use(function (req) {
  req.rawBody = "";
  req.setEncoding("utf8");
  req.on("data", function(chunk) {
    req.rawBody += chunk;
  });
  req.on("end", function () {
   req.next();
  });
});

app.post("/v2/tx_sign_request", async (req, res) => {
  let verified;
  try {
  const tx = jwt.decode(req.rawBody);
  const { requestId } = tx;
  verified = jwt.verify(req.rawBody, cosignerPubKey);
  if (verified) {
    let action = "REJECT";
    let rejectionReason = "Logic returned false";
    const signedRes = jwt.sign(
      {
        action,
        requestId,
        rejectionReason
      },
      privateKey,
      { algorithm: "RS256" }
    );
    res.send(signedRes);
  }
  } catch (e) {
    res.sendStatus(401);
  }
});
app.listen(3000);
from pathlib import Path
from wsgiref.simple_server import make_server
import falcon
import jwt
callback_handler_prikey = None
cosigner_pubkey = None

# Load keys.
f1 = Path("callback_private.pem")
if f1.is_file(): callback_handler_prikey = f1.read_bytes()
f2 = Path("cosigner_public.pem")
if f2.is_file(): cosigner_pubkey = f2.read_bytes()

class JWTTransferRequest(object):
    def on_post(self, req, resp):
        raw_req = req.bounded_stream.read()
        req = jwt.decode(raw_req, cosigner_pubkey, algorithms=["RS256"])
        resp.body = jwt.encode({'action': 'APPROVE', 'requestId': req['requestId']}, callback_handler_prikey, algorithm="RS256")
        resp.status = falcon.HTTP_200

# Create falcon app
app = falcon.App()
app.add_route('/v2/tx_sign_request', JWTTransferRequest())
app.add_route('/v2/config_change_sign_request', JWTTransferRequest())
if __name__ == '__main__':
    with make_server('', 80, app) as httpd:
        print('Serving on port 80...')
        # Serve until process is killed
        httpd.serve_forever()
JWTTransferRequest()