Offline Signer

The key never leaves your infrastructure

An MIT-licensed package that signs EVM, UTXO, Tron, XRP, Kaspa and Solana transactions locally. No API call, no API key, no network round trip — the private key stays in your process, because it never has a reason to leave it.

npm i @cryptoapis-io/offline-signer
import { evmSignFromDetails } from "@cryptoapis-io/offline-signer/evm";

// data.item from POST /prepare-transactions/evm/ethereum/mainnet/native-coins
const { signedTransactionHex } = await evmSignFromDetails({
  blockchain: "ethereum",
  network: "mainnet",
  privateKey: process.env.PRIVATE_KEY, // never leaves this process
  toAddress: item.recipient,
  value: item.value,
  nonce: item.nonce,
  gasLimit: item.gasLimit,
});

// hand signedTransactionHex to POST /broadcast-transactions

Where it sits in the flow

Three steps, and only the middle one touches a key. Prepare and broadcast are ordinary REST calls; signing happens in your process, offline.

  1. POST /prepare-transactions/… returns an unsigned transaction
  2. Sign it here, offline, with your key — no network call
  3. POST /broadcast-transactions/… puts it on chain
MIT licensed
Six chain families
Zero API keys

Why signing belongs on your side

A provider that can sign for you is a provider that can move your funds — and one whose breach becomes your breach. Splitting the work is what removes that: we prepare the transaction and broadcast it, you sign it.

  • No custody risk — We never hold your keys, so there is nothing of yours for us to lose. Your security posture does not inherit ours.
  • Simpler compliance — Many licences and audits turn on who controls the keys. Keeping them in your own infrastructure keeps that answer short.
  • No lock-in — The package is MIT and works standalone. If you stop using our APIs tomorrow, your signing code keeps working.
{
  "blockchain": "ethereum",
  "network": "mainnet",
  "privateKey": "0x…",
  "toAddress": "0x7f4a…",
  "value": "1000000000000000",
  "nonce": 7,
  "gasLimit": "21000"
}

Broadcast

{
  "data": {
    "item": {
      "signedTransactionHex": "0xf86607…"
    }
  }
}

Response

{
  "data": {
    "item": {
      "transactionId": "0x9f2b…"
    }
  }
}
{
  "blockchain": "ethereum",
  "network": "mainnet",
  "privateKey": "0x…",
  "toAddress": "0x7f4a…",
  "value": "1000000000000000",
  "nonce": 7,
  "gasLimit": "21000"
}

Where it sits in the flow

Three steps, and only the middle one touches a key. Prepare and broadcast are ordinary REST calls; signing happens in your process, offline.

  1. POST /prepare-transactions/… returns an unsigned transaction
  2. Sign it here, offline, with your key — no network call
  3. POST /broadcast-transactions/… puts it on chain

Chains it signs

Six families, one import path each, so signing EVM never pulls in the Bitcoin, Zcash or Solana stacks. Every chain signs differently — this is the part you would otherwise maintain yourself.

EVM

Ethereum, Ethereum Classic, BNB Smart Chain, Polygon, Avalanche, Arbitrum, Base, Optimism, Tron (EVM)

UTXO

Bitcoin, Bitcoin Cash, Litecoin, Dogecoin, Dash, Zcash

Tron

Native TRX and TRC transactions

XRP

XRP Ledger payments

Kaspa

Native Kaspa signing

Solana

Partial / fee-payer signing

Read the signing docs

Per-family signatures, the fields each one expects, and what it returns.

View on GitHub

Built for teams like yours

Wallets, exchanges, payment processors and compliance teams run on this API. Find the one closest to what you do and see which products it uses.

Crypto Mining
Crypto Wallet
Crypto PSP
Block Explorer
Crypto Custody
Portfolio Tracking
Crypto Taxes & Accounting
Crypto Exchange

You might also be interested in