Wallets API

Create and manage HD master wallets across multiple blockchains. Master wallets are the parent of all child addresses on a chain/network.

Authentication:

  • HMAC-only for POST /wallets
  • Dual-auth (JWT or HMAC) for all reads and balance lookups

Base path: /api/v1/wallets


Create master wallet

POST /api/v1/wallets

Auth: HMAC-only.

Request body

Field Type Required Description
chain string Yes ethereum, polygon, tron, solana, bitcoin
network string Yes mainnet, sepolia, amoy, shasta, etc. — see supported networks
label string No Friendly name

Example

curl -X POST https://api.hasapay.com/api/v1/wallets \
  -H "X-API-Key: $API_KEY" \
  -H "X-Signature: $SIG" \
  -H "X-Timestamp: $TS" \
  -H "X-Request-ID: $RID" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "ethereum",
    "network": "sepolia",
    "label": "Main ETH Wallet"
  }'

Response

{
  "data": {
    "id": "uuid",
    "organization_id": "uuid",
    "chain": "ethereum",
    "network": "sepolia",
    "address": "0xd502b72b8D969D60D1094174b1457C73671eb9d8",
    "label": "Main ETH Wallet",
    "is_active": true,
    "child_count": 0,
    "created_at": "2026-06-09T10:00:00Z",
    "updated_at": "2026-06-09T10:00:00Z"
  }
}

Only one master wallet per (organization, chain, network). Retrying create returns the existing wallet, not an error.


List master wallets

GET /api/v1/wallets

Auth: Dual-auth.

Query params

Param Default Description
include_balances false Inline current balances per wallet

Response

{
  "data": [
    {
      "id": "uuid",
      "chain": "ethereum",
      "network": "sepolia",
      "address": "0xd502b72b...",
      "label": "Main ETH Wallet",
      "is_active": true,
      "child_count": 5,
      "balances": [
        {
          "asset_id": "uuid",
          "token_symbol": "ETH",
          "balance": "0.5",
          "balance_raw": "500000000000000000",
          "is_stale": false,
          "last_synced": "2026-06-09T10:30:00Z"
        }
      ],
      "created_at": "2026-06-09T10:00:00Z"
    }
  ],
  "count": 3
}

Get master wallet

GET /api/v1/wallets/:walletId

Auth: Dual-auth.

Query params

Param Default Description
include_balances false Inline balances

Returns a single wallet in the same shape as the list rows.


Get single-token balance

GET /api/v1/wallets/:walletId/balance

Auth: Dual-auth.

Query params

Param Required Description
chain Yes Chain of the asset
network Yes Network of the asset
token Yes Token symbol (e.g. USDC)
token_address No Contract address for ambiguous symbols (e.g. multiple USDCs on a chain)

Response

{
  "data": {
    "chain": "ethereum",
    "network": "sepolia",
    "token_symbol": "USDC",
    "balance": "1000.00",
    "balance_raw": "1000000000",
    "pending_in": "0",
    "pending_in_raw": "0",
    "pending_out": "0",
    "pending_out_raw": "0"
  }
}

pending_in is the sum of unconfirmed deposits; pending_out is the sum of unconfirmed withdrawals/sweeps. Both let you display a "pending balance" without waiting for confirmations.


Get all balances for a wallet

GET /api/v1/wallets/:walletId/balances

Auth: Dual-auth.

Returns every token balance the wallet holds on its chain/network.

{
  "data": [
    {
      "asset_id": "uuid",
      "chain": "ethereum",
      "network": "sepolia",
      "token_symbol": "ETH",
      "token_decimals": 18,
      "balance": "0.5",
      "balance_raw": "500000000000000000",
      "is_stale": false,
      "last_synced": "2026-06-09T10:30:00Z"
    },
    {
      "asset_id": "uuid",
      "chain": "ethereum",
      "network": "sepolia",
      "token_symbol": "USDC",
      "token_decimals": 6,
      "balance": "1000.00",
      "balance_raw": "1000000000",
      "is_stale": false,
      "last_synced": "2026-06-09T10:30:00Z"
    }
  ]
}

Fee wallet (gas tank)

Each organization has a per-chain fee wallet — a dedicated address that funds the gas/energy/bandwidth for sweeps and gasless token transfers on that chain. You fund it with native token (ETH, MATIC, TRX, SOL, BTC); HasaPay draws from it to pay network fees so your master/child balances stay intact.

GET  /api/v1/wallets/:walletId/fee-wallet
POST /api/v1/wallets/:walletId/fee-wallet

Auth: Dual-auth.

GET returns the fee wallet for the parent master wallet's chain/network, creating it on first access. POST explicitly creates it (idempotent — returns the existing one if already present).

{
  "data": {
    "id": "uuid",
    "master_wallet_id": "uuid",
    "chain": "ethereum",
    "network": "mainnet",
    "address": "0x…",
    "native_balance": "0.0",
    "created_at": "2026-06-19T10:30:00Z"
  }
}

Keep the fee wallet topped up with native token on mainnet — if it runs dry, sweeps and token withdrawals can't be broadcast.


Supported chains and networks

Chain Mainnet Testnet
ethereum mainnet sepolia
polygon mainnet amoy
tron mainnet shasta
solana mainnet devnet
bitcoin mainnet testnet

Which network a wallet lands on is determined by your mode: sandbox keys / test mode create testnet wallets, production keys / live mode create mainnet wallets.


Errors

Code Cause
WALLET_NOT_FOUND No wallet with that ID on this org
WALLET_ALREADY_EXISTS Returned existing — see note under create
INVALID_CHAIN Unsupported chain string
INVALID_NETWORK Unsupported network for this chain