Transactions API
View transaction history and send outgoing transactions.
Authentication:
- Dual-auth (JWT or HMAC) for all reads
- HMAC-only for all sends and gas estimates
Base path: /api/v1/transactions
List transactions
GET /api/v1/transactions
Auth: Dual-auth.
Query params
| Param | Type | Description |
|---|---|---|
type |
string | deposit, withdrawal, transfer, sweep |
status |
string | See statuses below |
chain |
string | Filter by chain |
network |
string | Filter by network |
wallet_id |
string | Filter by master wallet ID |
address_id |
string | Filter by child address ID |
asset_id |
string | Filter by asset |
from_date |
string | ISO 8601 lower bound |
to_date |
string | ISO 8601 upper bound |
limit |
integer | Default 50 |
offset |
integer | Default 0 |
Response
{
"data": [ /* Transaction[] */ ],
"meta": { "limit": 50, "offset": 0, "count": 132 }
}
Transaction shape
{
"id": "uuid",
"organization_id": "uuid",
"type": "deposit",
"status": "confirmed",
"chain": "ethereum",
"network": "sepolia",
"from_address": "0x...",
"to_address": "0x...",
"amount": "100.00",
"amount_raw": "100000000",
"tx_hash": "0xabc...",
"block_number": 12345678,
"confirmations": 12,
"required_confirmations": 12,
"asset_id": "uuid",
"token_symbol": "USDC",
"token_decimals": 6,
"wallet_id": "uuid",
"address_id": "uuid",
"created_at": "2026-06-09T10:10:00Z",
"updated_at": "2026-06-09T10:15:00Z"
}
Get one transaction
GET /api/v1/transactions/:id
Auth: Dual-auth.
Returns the same Transaction shape as above.
Get transaction status (lightweight poll)
GET /api/v1/transactions/:id/status
Auth: Dual-auth.
Response
{
"data": {
"id": "uuid",
"status": "confirming",
"confirmations": 3,
"required_confirmations": 12
}
}
Use this for polling — it skips the heavier joins the full GET does.
Look up by on-chain hash
GET /api/v1/transactions/hash/:hash
Auth: Dual-auth.
Returns the Transaction matching that tx_hash on the caller's organization. 404 if the hash isn't recognized.
Send from master wallet
POST /api/v1/wallets/:walletId/send
Auth: HMAC-only.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to_address |
string | Yes | Destination address |
amount |
string | Yes | Raw token units (e.g. 100000000 for 100 USDC at 6 decimals) |
asset_id |
string (UUID) | Yes | The asset to send — fetch from GET /assets |
idempotency_key |
string | No | Client-supplied idempotency key |
The send body does not take
chain,network, ortokenstrings. Theasset_idresolves chain + network + token + decimals on the server. Get asset IDs fromGET /api/v1/assets.
Example
curl -X POST https://api.hasapay.com/api/v1/wallets/{walletId}/send \
-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 '{
"to_address": "0x9876...",
"amount": "50000000",
"asset_id": "uuid-of-usdc-sepolia",
"idempotency_key": "payout_2026_06_09_001"
}'
Response
{
"success": true,
"message": "Transaction created and queued (sent from master wallet)",
"data": {
"id": "uuid",
"type": "withdrawal",
"status": "pending",
"chain": "ethereum",
"network": "sepolia",
"from_address": "0x742d35Cc...",
"to_address": "0x9876...",
"amount": "50.00",
"amount_raw": "50000000",
"asset_id": "uuid",
"token_symbol": "USDC",
"created_at": "2026-06-09T11:00:00Z"
}
}
The transaction begins life at status: pending and progresses through broadcasted → confirming → confirmed → completed. Listen for withdrawal.completed (or .failed) via webhooks rather than polling.
Send from a child address
POST /api/v1/wallets/:walletId/addresses/:addressId/send
Auth: HMAC-only.
Same request body as above (to_address, amount, asset_id, idempotency_key). Used when you want to send out of a specific child address rather than the master wallet — typically for routed-child withdrawals.
Estimate gas
POST /api/v1/wallets/:walletId/addresses/:addressId/estimate-gas
Auth: HMAC-only.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
to_address |
string | Yes | Destination |
amount |
string | Yes | Raw token units |
asset_id |
string | No | Omit for a native-token estimate |
For pure fee preview (no gas estimation, no on-chain probe) use GET /fees/estimate instead — that walks the org's fee resolution chain and returns a usable quote without needing an address.
Statuses
| Status | Meaning |
|---|---|
pending |
Created, awaiting broadcast (withdrawal) or first confirmation (deposit) |
broadcasted |
On-chain, but not yet seen by a confirmation poller |
confirming |
Picked up by node, confirmations accruing |
confirmed |
Reached required_confirmations |
completed |
Reached terminal state with all post-processing done |
failed |
Reverted, dropped, or expired |
dropped |
Evicted from mempool |
cancelled |
Cancelled by user (rare) |
Typical flow
Deposits: pending → confirmed → completed
Withdrawals: pending → broadcasted → confirming → confirmed → completed (or failed / dropped at any point past pending)
Sweeps: same flow as withdrawals
Types
| Type | Description |
|---|---|
deposit |
Incoming funds to a child address (external sender) |
withdrawal |
Outgoing funds sent via the API |
transfer |
Internal transfer between addresses (rarely surfaced) |
sweep |
Auto/manual sweep from child → master |
Errors
| Code | Cause |
|---|---|
TRANSACTION_NOT_FOUND |
No transaction with that ID on this org |
INSUFFICIENT_BALANCE |
Source doesn't have enough funds (including gas) |
INVALID_ADDRESS |
Destination doesn't match the chain's address format |
INVALID_AMOUNT |
Amount can't be parsed as a positive integer |
ASSET_NOT_ENABLED |
Asset is not enabled for this org — call POST /assets/enable first |
WALLET_INACTIVE |
Wallet was deactivated |