Public API · Transaction watch
Transaction watch
A Tx watch follows one transaction hash on one network until it reaches the confirmations your account needs, fails, or turns out not to exist. You do not poll: a tx event is delivered to your webhook or WebSocket (if enabled) when the transaction is seen in a block, for each new confirmation, and when the watch ends; the watch stays readable here. This is not Address Watch: you add hashes you already know, and each watch ends once. All routes require signed headers (see Authentication) and use the base URL from the Overview. Success responses carry ok: true; failures carry ok: false, error_code and error.
Flow diagram
A transaction watch is for a hash you already know. It follows the hash to a final result and ends once. On the way you are told when the transaction is seen in a block and of each new confirmation.
How a watch ends and how you are told
A watch moves pending (hash not seen yet) to check (found in a block, waiting for confirmations) to confirmed or failed once the chain reaches confirm_at_height and the result is decided. A hash that never appears on the network ends as cancelled. If the transaction disappears from its block (a chain reorganization), the watch goes back to pending and the process starts over.
A watch tells you how it goes with tx events: detected when the transaction is seen in a block (confirmations 1; again from the new block if a reorganization moves it), confirmations for each new confirmation from the second up to the one before the number you need, then confirmed or failed when the watch ends, or cancelled when no node ever knew the hash. With one confirmation needed you get detected and then the end. The events are delivered through the same channels as address events: the webhook (GET/PUT /v1/webhooks, a URL is required and the tx type follows the payment/payout network lists) and, if you enabled it, the WebSocket as event tx on your channel. See the Webhooks and WebSockets articles for signing, retries and the common header (schema_version, delivery_id, type, event, network).
A transaction is charged once, with its first event (detected): the tx watch webhook credits of the network (see network credits). The confirmations, confirmed and failed events that follow are free. A hash that no node ever knew ends as cancelled and is charged as one delivery. A transaction that failed on chain is as final as a successful one and is reported as event failed.
The body has the common header plus tx_hash and watch_id. For every event but cancelled it also carries the transaction as found in its block: block_height, block_hash, block_time, from_address, to_address, status, method, token, currency, amount, amount_raw, fee, fee_raw, confirmations, required_confirmations and confirmed. confirmations is the number the event is about; confirmed is true only on the confirmed and failed events that end the watch. Unknown values are omitted, not sent as null. There is no address or direction: the watch is about a hash, not an address. For cancelled there is no block, so only the header, tx_hash and watch_id remain.
tx event (confirmed)
{
"schema_version": "2",
"delivery_id": "…",
"type": "tx",
"event": "confirmed",
"network": "ethereum",
"tx_hash": "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
"block_height": 21540010,
"block_hash": "0x…",
"block_time": 1787393700,
"watch_id": "7b1f5a52-52c0-4c4d-a1f0-0e5f2d6c9a31",
"from_address": "0x…",
"to_address": "0x…",
"status": "success",
"method": "transfer",
"amount_raw": "30000000",
"confirmations": 12,
"required_confirmations": 12,
"confirmed": true
}Errors
Failed calls answer { ok: false, error_code, error } with an HTTP status. A request that cannot be accepted (wrong body, bad query value) is 400 with an errors list: every field that failed is named in it, and error_code is common.validation_failed when more than one field failed.
watch.tx_hash_required (400): network_slug or tx_hash is empty or too long. watch.scan_not_enabled (400): the network key cannot be watched. watch.no_plan (400): the account has no plan. watch.invalid_tx_status (400): the status filter is not one of the statuses above.
watch.tx_unavailable (403): the plan has no tx watch slots. watch.tx_limit (403): all slots are held by open watches; delete one or wait for it to end.
watch.ingest_unavailable (503): temporarily unavailable. On add, the watch is not created and holds no slot, so retry later; on delete, retry the delete.
Slots exhausted
{
"ok": false,
"error_code": "watch.tx_limit",
"error": "Tx watch slots limit reached (10). Upgrade your plan to add more."
}GET /v1/tx-watches
List your tx watches, newest first: the open ones and the ones that ended. Also returns the slots of the plan.
Sorgu parametreleri
| Ad | Tür | Açıklama |
|---|---|---|
| network_slug | string | Exact watch network key (trimmed, lowercased), e.g. ethereum. Max 255. (isteğe bağlı) |
| status | string | pending, check, stopped, confirmed, failed or cancelled. Anything else returns 400 watch.invalid_tx_status. (isteğe bağlı) |
| tx_hash | string | Case-insensitive prefix of the transaction hash. Max 255. (isteğe bağlı) |
| page | integer | Page number, 1–10000 (default 1). (isteğe bağlı) |
| per_page | integer | Page size, 1–100 (default 20). (isteğe bağlı) |
Yanıt alanları
| Ad | Tür | Açıklama |
|---|---|---|
| items[].watch_id | string | Public watch identifier (uuid). Also sent as watch_id in the tx event. Use it to delete the watch. |
| items[].network_slug | string | Watch network key: the base slug on mainnet (ethereum), {network}-{env} elsewhere (ethereum-sepolia). |
| items[].tx_hash | string | Transaction hash, trimmed and lowercased. |
| items[].status | string | pending (not seen in a block yet), check (seen, waiting for confirmations), confirmed, failed (the transaction failed on chain) or cancelled (the hash never appeared on the network). |
| items[].start_height | number | Height of the block the transaction was found in. 0 until it is found. |
| items[].confirm_at_height | number | Chain height at which the result is decided (block height plus required confirmations). While the watch is still pending it holds the number of confirmations required instead. |
| items[].required_confirmations | number | Confirmations the watch waits for: your account setting for the network, or the network default (at least 1). |
| items[].confirmations | number | Confirmations the transaction has now. The block that holds it is the first one, so it is 0 only while the transaction is not in a block (pending, cancelled). It grows while the watch is in check and stops at the block that decided the result once the watch has ended (confirmed, failed). |
| items[].confirmed | boolean | true once the watch has ended with its confirmations: status confirmed, or failed (a transaction that failed on chain is as final as a successful one; status tells which). false while the watch is pending or in check, and for cancelled. |
| items[].final_block | number | Chain height at which the result is decided (the block of the transaction plus required_confirmations). Absent until a block has shown the transaction. (isteğe bağlı) |
| items[].network_fee | string | Fee paid, in the network coin as a decimal string. Absent until the watch ends with a result. (isteğe bağlı) |
| items[].checked_height | number | Chain height at which the transaction was last checked. Absent before the first check. (isteğe bağlı) |
| items[].created_at | string | When the watch was created, ISO 8601 with offset. |
| slots | number | Plan cap for concurrently open tx watches. 0 means Tx Watch is not available on the plan. |
| slots_used | number | Open watches (pending or check) that hold a slot now. |
| pagination.page | number | Current page. |
| pagination.per_page | number | Page size used for this response. |
| pagination.total | number | Total number of matching watches. |
| pagination.has_more | boolean | True if more pages exist after this one. |
- Sign the path together with the query string (see Authentication): the query is part of the signature.
- A page, per_page or text value outside its bounds is rejected with 400 and an errors list naming the parameter; it is never replaced by a default.
- Open watches (pending, check) have no time limit and each holds one slot. Ended ones (confirmed, failed, cancelled) hold no slot.
- slots_used counts only open watches, so it is not the same as pagination.total.
İstek örneği
import { createHash, createHmac } from 'node:crypto'
const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'
// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
const timestamp = Math.floor(Date.now() / 1000)
const bodyHash = createHash('sha256').update(body).digest('hex')
const signature = createHmac('sha256', SECRET)
.update([timestamp, method, path, bodyHash].join('\n'))
.digest('hex')
const res = await fetch(BASE + path, {
method,
headers: {
'X-Api-Key': API_KEY,
'X-Timestamp': String(timestamp),
'X-Signature': signature,
...(body ? { 'Content-Type': 'application/json' } : {}),
},
body: body || undefined,
})
return res.json()
}
console.log(await call('GET', '/v1/tx-watches'))İstek, kod örneğinin içinde imzalanır — imzalama kuralları için bkz. Authentication.
Yanıt örneği
{
"ok": true,
"items": [
{
"watch_id": "7b1f5a52-52c0-4c4d-a1f0-0e5f2d6c9a31",
"network_slug": "ethereum",
"tx_hash": "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
"status": "check",
"start_height": 21540010,
"confirm_at_height": 21540022,
"required_confirmations": 12,
"confirmations": 5,
"confirmed": false,
"final_block": 21540022,
"checked_height": 21540014,
"created_at": "2026-08-22T10:15:00+00:00"
},
{
"watch_id": "c9fd242d-525b-4b81-9a99-b2a95be331c9",
"network_slug": "tron",
"tx_hash": "bb08e86bca3219613b2d10f8b731c139aebc74c7a85f2a736b18bed50a2c320c",
"status": "confirmed",
"start_height": 85558636,
"confirm_at_height": 85558655,
"required_confirmations": 19,
"confirmations": 20,
"confirmed": true,
"final_block": 85558655,
"network_fee": "13.3",
"checked_height": 85558655,
"created_at": "2026-08-22T09:40:12+00:00"
}
],
"slots": 10,
"slots_used": 1,
"pagination": {
"page": 1,
"per_page": 20,
"total": 2,
"has_more": false
}
}POST /v1/tx-watches
Start watching a transaction hash on a network. Takes one tx slot until the watch ends.
İstek gövdesi
| Ad | Tür | Açıklama |
|---|---|---|
| network_slug | string | Watch network key (max 80, trimmed, lowercased). Mainnet is the base network slug (ethereum); other environments are {network}-{env} (ethereum-sepolia). The environment must be available for watching, otherwise the request fails with watch.scan_not_enabled. |
| tx_hash | string | Transaction hash to follow (max 128). It is trimmed and lowercased. |
Yanıt alanları
| Ad | Tür | Açıklama |
|---|---|---|
| item | object | The created (or already open) watch: the same fields as a list item. |
| item.watch_id | string | Public watch identifier (uuid). Also sent as watch_id in the tx event. Use it to delete the watch. |
| item.network_slug | string | Watch network key: the base slug on mainnet (ethereum), {network}-{env} elsewhere (ethereum-sepolia). |
| item.tx_hash | string | Transaction hash, trimmed and lowercased. |
| item.status | string | pending (not seen in a block yet), check (seen, waiting for confirmations), confirmed, failed (the transaction failed on chain) or cancelled (the hash never appeared on the network). |
| item.start_height | number | Height of the block the transaction was found in. 0 until it is found. |
| item.confirm_at_height | number | Chain height at which the result is decided (block height plus required confirmations). While the watch is still pending it holds the number of confirmations required instead. |
| item.required_confirmations | number | Confirmations the watch waits for: your account setting for the network, or the network default (at least 1). |
| item.confirmations | number | Confirmations the transaction has now. The block that holds it is the first one, so it is 0 only while the transaction is not in a block (pending, cancelled). It grows while the watch is in check and stops at the block that decided the result once the watch has ended (confirmed, failed). |
| item.confirmed | boolean | true once the watch has ended with its confirmations: status confirmed, or failed (a transaction that failed on chain is as final as a successful one; status tells which). false while the watch is pending or in check, and for cancelled. |
| item.final_block | number | Chain height at which the result is decided (the block of the transaction plus required_confirmations). Absent until a block has shown the transaction. (isteğe bağlı) |
| item.network_fee | string | Fee paid, in the network coin as a decimal string. Absent until the watch ends with a result. (isteğe bağlı) |
| item.checked_height | number | Chain height at which the transaction was last checked. Absent before the first check. (isteğe bağlı) |
| item.created_at | string | When the watch was created, ISO 8601 with offset. |
- Body fields are exact: a misspelled or unknown field is rejected with 400 and named in errors. Send Content-Type: application/json and sign the raw body.
- The hash may be added before it reaches the network: the watch starts as pending. A hash that never appears on the network ends as cancelled.
- A hash that is already being watched is not an error: the open watch is returned and no extra slot is taken. A hash whose earlier watch ended is watched again as a new watch (new watch_id) and replaces the old one in the list.
- Required confirmations come from your account setting for that network, or the network default (at least 1). For a pending watch confirm_at_height holds that number; once the transaction is found it becomes the block height plus required confirmations.
İstek örneği
import { createHash, createHmac } from 'node:crypto'
const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'
// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
const timestamp = Math.floor(Date.now() / 1000)
const bodyHash = createHash('sha256').update(body).digest('hex')
const signature = createHmac('sha256', SECRET)
.update([timestamp, method, path, bodyHash].join('\n'))
.digest('hex')
const res = await fetch(BASE + path, {
method,
headers: {
'X-Api-Key': API_KEY,
'X-Timestamp': String(timestamp),
'X-Signature': signature,
...(body ? { 'Content-Type': 'application/json' } : {}),
},
body: body || undefined,
})
return res.json()
}
console.log(await call('POST', '/v1/tx-watches', '{"network_slug":"string","tx_hash":"string"}'))İstek, kod örneğinin içinde imzalanır — imzalama kuralları için bkz. Authentication.
Yanıt örneği
{
"ok": true,
"item": {
"watch_id": "7b1f5a52-52c0-4c4d-a1f0-0e5f2d6c9a31",
"network_slug": "ethereum",
"tx_hash": "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
"status": "pending",
"start_height": 0,
"confirm_at_height": 12,
"required_confirmations": 12,
"confirmations": 0,
"confirmed": false,
"created_at": "2026-08-22T10:15:00+00:00"
}
}DELETE /v1/tx-watches/{watchId}
Stop watching a transaction and remove the watch. The watch id goes in the path; there is no request body.
- watchId is the watch_id returned by add or list (a uuid).
- Deleting is idempotent: an unknown, foreign, malformed or already removed id still returns ok: true. An ended watch can be deleted too; it disappears from the list.
- A deleted open watch frees its slot and no tx event is sent for it.
İstek örneği
import { createHash, createHmac } from 'node:crypto'
const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'
// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
const timestamp = Math.floor(Date.now() / 1000)
const bodyHash = createHash('sha256').update(body).digest('hex')
const signature = createHmac('sha256', SECRET)
.update([timestamp, method, path, bodyHash].join('\n'))
.digest('hex')
const res = await fetch(BASE + path, {
method,
headers: {
'X-Api-Key': API_KEY,
'X-Timestamp': String(timestamp),
'X-Signature': signature,
...(body ? { 'Content-Type': 'application/json' } : {}),
},
body: body || undefined,
})
return res.json()
}
console.log(await call('DELETE', '/v1/tx-watches/00000000-0000-0000-0000-000000000000'))İstek, kod örneğinin içinde imzalanır — imzalama kuralları için bkz. Authentication.
Yanıt örneği
{
"ok": true
}İpuçları
- network_slug is the watch network key: the base catalog slug on mainnet (ethereum, tron) and {network}-{env} on other environments (ethereum-sepolia, tron-nile).
- Add the hash right after you broadcast it; there is no need to wait until it appears on chain. Remember that every open watch holds a slot until it ends.
- DELETE takes the watch id in the path (/v1/tx-watches/{watchId}) and no body.
- Fields without a value are not written. Read an absent network_fee or checked_height as "not known yet".
- Listing is a convenience, not the delivery channel: rely on the webhook or WebSocket event and use the list to reconcile.