Address transfer monitoring — API reference
Transaction query module
The transaction query module provides multi-dimensional queries for on-chain transaction data, including historical lists by address, batch transaction detail queries, and cross-chain aggregate queries.
API list
| Method | Path | Description |
|---|---|---|
| POST | /tx/api/v1/transfers/query | Query transaction list by address with cursor pagination |
| POST | /tx/api/v1/transfers/multi-chain | Multi-chain and multi-address aggregate query |
| POST | /tx/api/v1/transfers/details | Batch query details by transaction hash |
| GET | /tx/api/v1/transfers/detail | Query single transaction detail, V2 |
Address transaction query
API path: POST /tx/api/v1/transfers/query
Purpose
Queries the historical transaction list for one address on a specified chain. It supports filtering by transaction type and token, and uses cursor-based pagination.
Request body fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
address | string | Yes | - | Wallet address to query. |
chain_type | string | No | - | Chain type. If omitted, the service performs a cross-chain query. |
chain_id | integer | No | - | Chain ID. |
tx_type | string | No | "" | Transaction type filter. Empty string means no filtering. |
token | string | No | - | Token contract address used to filter a specific token transfer. |
limit | integer | No | 20 | Maximum number of returned records. Range: 1~100. |
id | integer | No | - | Cursor ID for pagination continuation. |
Example
# Basic query.
curl -X POST "$BASE_URL/tx/api/v1/transfers/query" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"chain_type": "ethereum",
"chain_id": 1,
"address": "0x1234567890abcdef1234567890abcdef12345678",
"limit": 20
}'
# Filter by token to query USDT transfers.
curl -X POST "$BASE_URL/tx/api/v1/transfers/query" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"chain_type": "ethereum",
"chain_id": 1,
"address": "0x1234...",
"token": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}'
Response
{
"code": 1,
"msg": "success",
"data": [
{
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"tx_time": 1745000000,
"tx_hash": "0xabc123...",
"sender": "0x1234...",
"receiver": "0x5678...",
"amount": "1000000000000000000",
"symbol": "ETH",
"decimals": 18,
"tx_status": "success",
"transfer_type": "native",
"token_transfers": [],
"from_details": [],
"to_details": [],
"internal_transactions": []
}
]
}
This API returns
code: 1on success and usesmsg, which differs from some other APIs.
Multi-address aggregate query
API path: POST /tx/api/v1/transfers/multi-chain
Purpose
Queries transaction records for multiple chains and multiple addresses and returns the aggregated result.
Request body fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
addresses | array | Yes | - | Multi-chain address groups. At least 1 group and at most 10 groups. |
addresses[].chain_type | string | Yes | - | Chain type. |
addresses[].chain_id | integer | Yes | - | Chain ID. |
addresses[].address | string[] | Yes | - | Address array for this chain. At most 20 addresses per group. |
limit | integer | No | 20 | Maximum returned records. Range: 1~100. |
id | integer | No | - | Cursor ID for pagination continuation. |
Example
curl -X POST "$BASE_URL/tx/api/v1/transfers/multi-chain" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"addresses": [
{"chain_type": "ethereum", "chain_id": 1, "address": ["0xabc...001", "0xabc...002"]},
{"chain_type": "bsc", "chain_id": 56, "address": ["0xdef...003"]},
{"chain_type": "solana", "chain_id": 1, "address": ["SolanaAddress..."]}
],
"limit": 20
}'
Response
The response format is the same as /tx/api/v1/transfers/query. data is the aggregated transaction record array.
Batch transaction detail query
API path: POST /tx/api/v1/transfers/details
Purpose
Batch queries transaction details by transaction hash.
Request body fields
| Field | Type | Required | Description |
|---|---|---|---|
chain_type | string | Yes | Chain type. |
chain_id | integer | Yes | Chain ID. |
tx_hash | string or string[] | Yes | A single transaction hash or an array of hashes. |
Example
# Query one transaction.
curl -X POST "$BASE_URL/tx/api/v1/transfers/details" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"chain_type": "ethereum", "chain_id": 1, "tx_hash": "0xabc123..."}'
# Batch query multiple transactions.
curl -X POST "$BASE_URL/tx/api/v1/transfers/details" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"chain_type": "ethereum", "chain_id": 1, "tx_hash": ["0xabc123...", "0xdef456..."]}'
Single transaction detail query
API path: GET /tx/api/v1/transfers/detail
Purpose
Queries the full details of one transaction, V2. Main differences from /tx/api/v1/transfers/details:
- Uses GET with query string parameters.
- Supports
no_cacheto force an origin fetch. If there is no local database record, the service can fetch by chain and write the result into cache. - Writes the data into the database cache after a successful origin fetch.
- Returns a mixed field naming structure for historical compatibility. Some fields are camelCase.
Request parameters
| Name | Location | Type | Required | Description |
|---|---|---|---|---|
chain_type | query | string | Yes | Chain type. |
chain_id | query | integer | Yes | Chain ID. |
tx_hash | query | string | Yes | Transaction hash, with or without the 0x prefix. |
no_cache | query | string | No | Pass true or 1 to skip local cache and fetch from origin. |
Example
# Normal query.
curl "$BASE_URL/tx/api/v1/transfers/detail?chain_type=ethereum&chain_id=1&tx_hash=0xabc123..."
# Force origin fetch.
curl "$BASE_URL/tx/api/v1/transfers/detail?chain_type=ethereum&chain_id=1&tx_hash=0xabc123...&no_cache=true"
Response when the transaction is found
{
"code": 0,
"msg": "success",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"txTime": 1745000000000,
"txhash": "0xabc123...",
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0x5678...",
"transfer_type": "native",
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "20000000000",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"txFee": "420000000000000",
"nonce": "42",
"symbol": "ETH",
"decimals": 18,
"amount": "1000000000000000000",
"txStatus": "success",
"methodId": "",
"methodCall": "",
"l1OriginHash": "",
"fromDetails": [],
"toDetails": [],
"internalTransactionDetails": [],
"tokenTransferDetails": [],
"listenAddress": null,
"is_reorg_resend": false
}
}
Response when the transaction does not exist
{
"code": 0,
"msg": "success",
"data": null
}
Transaction data structure
TransactionDetail returned by list queries
The /tx/api/v1/transfers/query, /tx/api/v1/transfers/multi-chain, and /tx/api/v1/transfers/details APIs return this transaction format.
Main fields:
| Field | Type | Description |
|---|---|---|
chain_type | string | Normalized lowercase chain type. |
chain_id | integer | Chain ID. |
height | integer | null | Block height. |
tx_time | integer | null | Transaction time as a second-level Unix timestamp. |
tx_hash | string | Transaction hash. |
tx_version | integer | null | Transaction version, used by chains such as Solana. |
sender | string | null | Sender address. |
fee_payer | string | null | Fee payer address, mainly for Solana. |
receiver | string | null | Receiver address. |
transfer_type | string | null | Transaction type, such as native or token. |
amount | string | null | Transaction amount as a string to avoid precision loss. |
symbol | string | null | Native currency or token symbol, such as ETH or BNB. |
decimals | integer | null | Decimal places, such as 18. |
tx_status | string | null | Transaction status: success, failed, or pending. |
tx_fee | string | null | Transaction fee. |
gas_limit | string | null | Gas limit for EVM chains. |
gas_used | string | null | Actual gas used for EVM chains. |
gas_price | string | null | Gas price for EVM chains. |
max_fee_per_gas | string | null | EIP-1559 max fee per gas. |
max_priority_fee_per_gas | string | null | EIP-1559 max priority fee per gas. |
nonce | string | null | Account nonce for EVM chains. |
method_id | string | null | Contract method selector for EVM calls. |
method_call | string | null | Contract method name or call summary. |
l1_origin_hash | string | null | Original L1 transaction hash for L2 chains. |
token_transfers | array | Token transfer details. |
from_details | array | Input address details for UTXO-style data. |
to_details | array | Output address details for UTXO-style data. |
internal_transactions | array | Internal transactions or Solana instruction details. |
token_transfers:
| Field | Type | Description |
|---|---|---|
from_address | string | null | Token source address. |
to_address | string | null | Token destination address. |
token_contract_address | string | null | Token contract address. |
symbol | string | null | Token symbol. |
amount | string | null | Transfer amount. |
decimals | integer | null | Token decimals. |
is_from_contract | boolean | Whether the source address is a contract. |
is_to_contract | boolean | Whether the destination address is a contract. |
from_token_address | string | null | Source token account address for Solana. |
to_token_address | string | null | Destination token account address for Solana. |
mint | string | null | Solana mint address. |
program_id | string | null | Solana program ID. |
from_details, input details for UTXO-style data:
| Field | Type | Description |
|---|---|---|
address | string | null | Input address. |
amount | string | null | Input amount. |
is_contract | boolean | Whether the address is a contract. |
vin_index | string | null | UTXO input index. |
pre_vout_index | string | null | Previous transaction output index. |
ref_tx_hash | string | null | Referenced previous transaction hash. |
to_details, output details for UTXO-style data:
| Field | Type | Description |
|---|---|---|
address | string | null | Output address. |
amount | string | null | Output amount. |
is_contract | boolean | Whether the address is a contract. |
vout_index | string | null | UTXO output index. |
internal_transactions, internal calls or Solana instructions:
| Field | Type | Description |
|---|---|---|
from_address | string | null | Internal call sender. |
to_address | string | null | Internal call receiver. |
amount | string | null | Internal call amount. |
tx_status | string | null | Internal call status. |
program_id | string | null | Solana program ID. |
instr_index | integer | null | Solana instruction index. |
depth | integer | null | Solana call depth. |
instruction | string | null | Solana instruction name. |
description | string | null | Instruction description. |
V2 field naming differences
The /tx/api/v1/transfers/detail API returns a mixed naming style for historical compatibility:
| V2 field | Matching list field | Main difference |
|---|---|---|
txhash | tx_hash | Different field name. |
txTime | tx_time | camelCase and millisecond timestamp. |
txStatus | tx_status | camelCase. |
txFee | tx_fee | camelCase. |
gasLimit | gas_limit | camelCase. |
gasUsed | gas_used | camelCase. |
gasPrice | gas_price | camelCase. |
methodId | method_id | camelCase. |
methodCall | method_call | camelCase. |
l1OriginHash | l1_origin_hash | camelCase. |
fromDetails | from_details | camelCase. |
toDetails | to_details | camelCase. |
internalTransactionDetails | internal_transactions | Different field name. |
tokenTransferDetails | token_transfers | Different field name. |
Pagination
Transaction list queries use cursor pagination instead of page numbers:
- Do not pass
idin the first request. The service returns the latest N records. - If the returned record count equals
limit, there may be more data. - Use the database internal
idof the last returned record as the next request'sid. - Continue until the returned record count is smaller than
limit.
Cursor pagination does not support jumping to a page. It can only move forward sequentially.
Amount handling recommendations
All amount fields are returned as strings to avoid large-number precision loss. Convert display values with the decimals field:
function formatAmount(amount, decimals) {
if (!amount || decimals === null) return '0';
const bigAmount = BigInt(amount);
const divisor = BigInt(10 ** decimals);
const intPart = bigAmount / divisor;
const fracPart = bigAmount % divisor;
return `${intPart}.${fracPart.toString().padStart(decimals, '0')}`;
}
// Example: 1000000000000000000 with decimals=18 becomes "1.000000000000000000".
console.log(formatAmount('1000000000000000000', 18));
DEX Pool recent trades API
The DEX Pool module maintains pool addresses that need monitoring and exposes a recent-trades query. Related subscriptions and pushes still use the address management APIs or WebSocket subscribe flow.
After an on-chain transaction is processed successfully, the service matches transaction participant addresses against enabled pool addresses. Matched transactions are written as full TransactionResult objects into the pool's Durable Object FIFO queue, which keeps up to the latest 100 records by default.
Query recent pool trades
API path: GET /tx/api/v1/dex-pools/{poolAddress}/trades
Purpose
Publicly queries recent trades for a specific pool. No additional administrator authentication is required.
Query parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
chainType | string | Yes | None | Chain type; chain_type is also accepted. |
chainId | number | Yes | None | Chain ID; chain_id is also accepted. |
limit | number | No | 100 | Number of recent trades to return, from 1 to 100. |
Example
curl "$BASE_URL/tx/api/v1/dex-pools/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8/trades?chainType=evm&chainId=1&limit=50"
Successful response
{
"code": 0,
"message": "ok",
"data": {
"items": [
{
"chainType": "evm",
"chainId": 1,
"height": 19600000,
"txTime": 1710000000,
"txHash": "0x...",
"sender": "0xsender...",
"feePayer": "0xsender...",
"receiver": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
"transferType": "Token",
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "1000000000",
"maxFeePerGas": "",
"maxPriorityFeePerGas": "",
"txFee": "21000000000000",
"nonce": "1",
"symbol": "ETH",
"decimals": 18,
"amount": "0",
"txStatus": "success",
"methodId": "0xa9059cbb",
"methodCall": "0x...",
"fromDetails": [],
"toDetails": [],
"internalTransactionDetails": [],
"tokenTransferDetails": [
{
"from": "0xsender...",
"to": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
"isFromContract": false,
"isToContract": false,
"tokenContractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"symbol": "USDC",
"amount": "1000000",
"decimals": 6
}
]
}
]
}
}
Returned transaction fields
data.items is TransactionResult[]. Field names match the array elements in the HTTP webhook body and do not use the WebSocket push names such as chain_type, txhash, or transfer_type. Optional fields whose value is undefined are omitted from JSON, while fields whose value is null are kept as null.
Main fields include: chainType, chainId, height, txTime, txHash, sender, feePayer, receiver, transferType, gasLimit, gasUsed, gasPrice, maxFeePerGas, maxPriorityFeePerGas, txFee, nonce, symbol, decimals, amount, txStatus, methodId, methodCall, fromDetails, toDetails, internalTransactionDetails, and tokenTransferDetails.
Address management module
The address management module maintains the monitored address list for each business integration. Only addresses added to the list trigger transaction pushes to the corresponding WebSocket subscribers or Webhook callback.
API list
| Method | Path | Description |
|---|---|---|
| PATCH | /tx/api/v1/addresses | Patch mode for adding and removing addresses in one request |
| POST | /tx/api/v1/addresses | Batch add addresses |
| POST | /tx/api/v1/addresses/batch-delete | Batch remove addresses |
| POST | /tx/api/v1/addresses/contains | Query whether an address exists in the list |
Public authentication notes
All protected address APIs identify the caller through X-API-Key. The service uses this key for API authentication, business ownership identification, and billing statistics.
| Method | Description | Example |
|---|---|---|
| Request header | Public API key | X-API-Key: your-api-key |
Public access only requires
X-API-Key. No additional application identity header or query parameter is required.
Add monitored addresses API
API path: POST /tx/api/v1/addresses
Purpose
Batch adds addresses to the monitored address list for the current business identity. Multiple chains can be added in one request.
Chain-specific behavior
- Other non-EVM chains: the service attempts to sync addresses to an internal platform monitoring component. Sync failures do not block local database writes.
Request body fields
| Field | Type | Required | Description |
|---|---|---|---|
wallets | array | Yes | Address list grouped by chain. At least one item is required. |
wallets[].chain_type | string | Yes | Chain type, such as ethereum, solana, or tron. |
wallets[].address | string[] | Yes | Address array for the chain. At least one address is required. |
Example
# Add Ethereum addresses.
curl -X POST "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{
"chain_type": "ethereum",
"address": ["0x1234...", "0xabcd..."]
}
]
}'
# Add addresses for multiple chains.
curl -X POST "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{"chain_type": "ethereum", "address": ["0x1234..."]},
{"chain_type": "tron", "address": ["TJDENsfBJs4RFETt1X1uyT9pBxdnAbFnbm"]},
{"chain_type": "solana", "address": ["SolanaAddress..."]}
]
}'
Response
{
"code": 0,
"message": "ok",
"data": "success"
}
Remove monitored addresses
API path: POST /tx/api/v1/addresses/batch-delete
Purpose
Batch removes addresses from the monitored address list. After removal, new transactions for the address are no longer pushed to the current business identity.
Request body fields
Same as /tx/api/v1/addresses: wallets[].chain_type plus wallets[].address.
Example
curl -X POST "$BASE_URL/tx/api/v1/addresses/batch-delete" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"wallets": [
{"chain_type": "ethereum", "address": ["0x1234..."]}
]
}'
Response
{
"code": 0,
"message": "ok",
"data": "success"
}
Patch address list
API path: PATCH /tx/api/v1/addresses
Purpose
Uses patch mode to add and remove addresses for one chain in a single request.
Request body fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
chain_type | string | Yes | - | Chain type. This API operates on one chain at a time. |
adds | string[] | No | [] | Addresses to add to the list. |
removes | string[] | No | [] | Addresses to remove from the list. |
Example
curl -X PATCH "$BASE_URL/tx/api/v1/addresses" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"chain_type": "ethereum",
"adds": ["0xNewAddress001...", "0xNewAddress002..."],
"removes": ["0xOldAddress001..."]
}'
Response
{
"code": 0,
"message": "ok",
"data": "success"
}
Address existence query
API path: POST /tx/api/v1/addresses/contains
Purpose
Queries whether an address already exists in the address list for a specified chain.
In the current version, this API only checks whether the address exists in the global address table. It does not filter by API key isolation.
Request body fields
| Field | Type | Required | Description |
|---|---|---|---|
chain_type | string | Yes | Chain type. |
address | string | Yes | Address to query. |
Example
curl -X POST "$BASE_URL/tx/api/v1/addresses/contains" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"chain_type": "ethereum", "address": "0x1234..."}'
Response
{
"code": 0,
"message": "ok",
"data": {
"is_exist": true
}
}
Address management best practices
Address format guidelines:
- EVM chains: use hex addresses starting with
0x. EIP-55 checksum format is recommended. - Solana: use Base58 addresses.
- Tron: use Base58Check addresses starting with
T. - Bitcoin: use standard Bitcoin address formats, such as P2PKH, P2SH, and Bech32.
Relationship between the address list and WebSocket subscriptions:
- Addresses added through
/tx/api/v1/addressesare persisted in the database and remain effective after service restarts. - After a WebSocket connection is established, the service automatically monitors all addresses in the business identity's address list.
- You can also use the WebSocket
subscribecommand to add temporary monitoring after connection establishment, but this is only effective for the current connection and is not restored after disconnection.
For regular business monitoring, use HTTP APIs to manage the address list. Use WebSocket subscribe and unsubscribe only for temporary fine-grained control.
WebSocket real-time push
The WebSocket module provides real-time on-chain transaction push. After a client establishes a WebSocket connection, the service immediately pushes messages when monitored addresses produce on-chain transactions.
Connection endpoint:
GET /tx/api/v1/transaction/ws
Establishing a connection
Request notes
The WebSocket handshake requires these request headers:
| Header | Type | Required | Description |
|---|---|---|---|
Upgrade | string | Yes | Fixed value: websocket. |
X-API-Key | string | Yes | Public API key for authentication and billing identification. |
X-ResendDuration | string | No | Historical message resend window in seconds. Default: 180. |
If
X-API-Keyis missing, the connection request returns400or an authentication failure.
Connection example
# Use wscat.
wscat -c "wss://api.gelabs.org/tx/api/v1/transaction/ws" \
-H "X-API-Key: your-api-key"
Successful connection message
After the connection is established, the server immediately pushes:
{
"id": "msg-uuid-001",
"time": 1745000000000,
"type": "connected",
"code": 0,
"data": {
"clientId": "client-abc123",
"appId": "current-business-identity"
},
"msg": "success"
}
| Field | Description |
|---|---|
data.clientId | Unique server-side identifier of the current connection. It changes after reconnecting. |
data.appId | Caller identity bound to the current connection. |
Connection failure
| HTTP status | Reason | Recommendation |
|---|---|---|
400 | API key not provided. | Confirm that X-API-Key is passed correctly. |
426 | Upgrade: websocket header is missing. | Confirm that the client connects through the WebSocket protocol. |
Message format
All WebSocket messages use JSON and follow this envelope:
{
"id": "message unique ID",
"time": 1745000000000,
"type": "message type",
"code": 0,
"data": {},
"msg": "success"
}
| Field | Type | Description |
|---|---|---|
id | string | Unique message ID generated by the sender. |
time | integer | Message timestamp as a millisecond Unix timestamp. |
type | string | Message type that determines business meaning. |
code | integer | Business status code. 0 means success and non-zero means error. |
data | object | null | Business data. |
msg | string | Text message. Successful responses usually use success. |
Client commands
Clients can send the commands below to the server. Each command needs a unique id, and the server response uses the same id.
ping (heartbeat)
Sends a heartbeat to keep the connection active.
Send:
{
"id": "ping-001",
"type": "ping",
"data": {}
}
Server response:
{
"id": "ping-001",
"time": 1745000000000,
"type": "pong",
"code": 0,
"data": null,
"msg": "success"
}
subscribe (subscribe to an address)
Subscribes to real-time transaction pushes for a specified address on a specified chain.
Addresses subscribed through this command are only effective for the current connection and are not saved after disconnection. For persistent address subscription, add the address through the address management module in advance.
Send:
{
"id": "cmd-001",
"type": "subscribe",
"data": {
"address": "0x1234567890abcdef1234567890abcdef12345678",
"chain_type": "ethereum"
}
}
Command parameters:
| Field | Type | Required | Description |
|---|---|---|---|
data.address | string | Yes | Address to subscribe. |
data.chain_type | string | Yes | Chain type. The server converts it to lowercase. |
Success response:
{
"id": "cmd-001",
"type": "subscribe",
"code": 0,
"data": {"API Key": "current-business-identity", "address": "0x1234...", "chain_type": "ethereum"},
"msg": "success"
}
Error response:
{
"id": "cmd-001",
"type": "subscribe",
"code": 400,
"data": null,
"msg": "address and chain_type required"
}
unsubscribe (unsubscribe)
Cancels the real-time transaction subscription for an address.
Send for a specific chain:
{
"id": "cmd-002",
"type": "unsubscribe",
"data": {
"address": "0x1234...",
"chain_type": "ethereum"
}
}
Send for all chains by omitting chain_type:
{
"id": "cmd-002",
"type": "unsubscribe",
"data": {
"address": "0x1234..."
}
}
Command parameters:
| Field | Type | Required | Description |
|---|---|---|---|
data.address | string | Yes | Address to unsubscribe. |
data.chain_type | string | No | Chain type. If omitted, all chain subscriptions for this address are canceled. |
Success response:
{
"id": "cmd-002",
"type": "unsubscribe",
"code": 0,
"data": {"address": "0x1234...", "chainType": "ethereum"},
"msg": "success"
}
When all chains are canceled, chainType is null.
getTxHash (query transaction details)
Queries the full details of one transaction through the WebSocket connection.
Send:
{
"id": "cmd-003",
"type": "getTxHash",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"txHash": "0xabc123..."
}
}
Command parameters:
| Field | Type | Required | Description |
|---|---|---|---|
data.chain_type | string | Yes | Chain type. |
data.chain_id | integer | Yes | Chain ID. |
data.txHash | string | No | Transaction hash. |
data.messageId | string | No | Unique ID of the real-time push message. Prefer this field when available. |
Prefer
messageId, theidvalue of the push message, so the server can locate the record more accurately.
Error response when the transaction does not exist:
{
"id": "cmd-003",
"type": "getTxHash",
"code": 404,
"data": null,
"msg": "transaction not found"
}
transactionACK (confirm processed transaction)
After receiving and processing a transaction message, the client should send an ACK. After receiving the ACK, the server stops retrying that transaction.
If the client does not send ACK for a long time, the server may resend unacknowledged transactions after the client reconnects to prevent message loss.
Send:
{
"id": "ack-001",
"type": "transactionACK",
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"txHash": "0xabc123..."
}
}
Success response:
{
"id": "ack-001",
"type": "transactionACK",
"code": 0,
"data": {"txHash": "0xabc123..."},
"msg": "success"
}
Server push messages
transaction (real-time transaction push)
When a monitored address has an on-chain transaction, the server pushes:
{
"id": "msg-tx-uuid-001",
"time": 1745000000000,
"type": "transaction",
"code": 0,
"data": {
"chain_type": "ethereum",
"chain_id": 1,
"height": 21000000,
"txTime": 1745000000000,
"txhash": "0xabc123...",
"sender": "0x1234...",
"fee_payer": "0x1234...",
"receiver": "0x5678...",
"transfer_type": "native",
"gasLimit": "21000",
"gasUsed": "21000",
"gasPrice": "20000000000",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1000000000",
"txFee": "420000000000000",
"nonce": "42",
"symbol": "ETH",
"decimals": 18,
"amount": "1000000000000000000",
"txStatus": "success",
"methodId": "",
"methodCall": "",
"fromDetails": [],
"toDetails": [],
"internalTransactionDetails": null,
"tokenTransferDetails": null,
"listenAddress": ["0x1234..."],
"is_reorg_resend": false
},
"msg": "success"
}
Key transaction message fields:
| Field | Type | Description |
|---|---|---|
data.listenAddress | string[] | Addresses that matched monitoring rules. One transaction may match multiple addresses. |
data.is_reorg_resend | boolean | Whether this is a resend after a chain reorganization. If true, check local records. |
data.tokenTransferDetails | array | null | Token transfer details. Present for ERC-20 transfers. |
data.txTime | integer | Transaction timestamp in milliseconds. |
is_reorg_resend notes:
When the value is true, the transaction is resent because of a chain reorganization. Check whether the local record for the same txhash already exists and update fields such as block height when needed.
ERC-20 token transfer message example:
{
"id": "msg-tx-002",
"type": "transaction",
"code": 0,
"data": {
"chain_type": "ethereum",
"txhash": "0xdef456...",
"transfer_type": "token",
"tokenTransferDetails": [
{
"from": "0x1234...",
"to": "0x5678...",
"isFromContract": false,
"isToContract": false,
"tokenContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"amount": "1000000000",
"decimals": 6,
"fromTokenAddress": "",
"toTokenAddress": "",
"mint": "",
"programId": ""
}
],
"listenAddress": ["0x1234..."],
"is_reorg_resend": false
},
"msg": "success"
}
HTTP helper APIs
WebSocket connection status query
API path: GET /tx/api/v1/transaction/ws/status
Queries the WebSocket connection status for the current business identity.
curl "$BASE_URL/tx/api/v1/transaction/ws/status" \
-H "X-API-Key: your-api-key"
The caller identity is identified by X-API-Key. Public access does not require an additional application identity query parameter.
Response:
{
"code": 0,
"message": "ok",
"data": {
"appId": "current-business-identity",
"connectedClients": 2,
"clients": ["client-abc123", "client-def456"],
"details": [
{
"clientId": "client-abc123",
"connectedAt": 1745000000000,
"uptime": 3600,
"pendingTxCount": 0
}
]
}
}
| Field | Type | Description |
|---|---|---|
connectedClients | integer | Number of online clients. |
details[].uptime | integer | Lifetime in seconds. |
details[].pendingTxCount | integer | Number of pending transactions that have not received ACK. |
WebSocket message replay
API path: POST /tx/api/v1/transaction/ws/replay
Manually replays a missed transaction message. The server will:
- Idempotently write the message into the database.
- Push it to currently online WebSocket subscribers.
- Trigger Webhook callbacks in parallel.
The request body is a complete WsTransactionMessage, the same format as the server-pushed transaction message.
Response:
{
"code": 0,
"message": "ok",
"data": {
"processed": true,
"txhash": "0xabc123..."
}
}
Error handling
WebSocket command errors use the same JSON envelope and set code to a non-zero value:
{
"id": "original command id",
"type": "command type",
"code": 400,
"data": null,
"msg": "error description"
}
Common errors:
| Command | code | msg |
|---|---|---|
subscribe | 400 | address and chain_type required |
unsubscribe | 400 | address required |
getTxHash | 400 | chain_type, chain_id, txHash required |
getTxHash | 404 | transaction not found |
transactionACK | 400 | chain_type and messageId/txHash required |
Best practices
Heartbeat
Send a ping command every 30 seconds:
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ id: `ping-${Date.now()}`, type: 'ping', data: {} }));
}
}, 30000);
Reconnect after disconnection
function connect() {
const ws = new WebSocket(WS_URL, { headers: { 'X-API-Key': API_KEY } });
ws.on('close', () => {
console.log('Connection closed, reconnecting in 5 seconds...');
setTimeout(connect, 5000);
});
return ws;
}
ACK strategy
- ACK promptly: after receiving and processing a
transactionmessage, sendtransactionACKimmediately. - Avoid missing ACK: if business processing is slow, consider sending ACK first and processing asynchronously.
- Deduplicate ACKs: use
txhash + chain_type + chain_idas the business idempotency key.
Address subscription strategy
| Method | Persistence | Use case |
|---|---|---|
HTTP address management (/tx/api/v1/addresses) | Persistent, effective after restart | Long-term monitoring of regular business addresses |
WebSocket subscribe command | Temporary, only effective for the current connection | Temporary debugging or dynamic fine-grained control |
Concurrent connections
The business identity represented by the same API key supports multiple clients connected at the same time. The server broadcasts transaction messages to all online connections. This is suitable for horizontal scaling or active-standby switching, as long as the business layer handles messages idempotently.