Real-Time Blockchain Notifications with Tatum: Stop Polling, Start Listening

Written by
Mohit Thakkar
June 3, 2026
5
min. read
Infographic titled “Real-Time Alerts – 4 Easy Steps” on a dark blue grid background. Three connected purple 3D blocks illustrate the workflow: 1. Incoming Data Event (database icon), 2. Trigger Function with Webhook (bell and API icons), and 3. Notificatio

A practical guide to event-driven blockchain architecture using webhooks

If you've ever built a blockchain application, you know the pain. You spin up a loop, poll the chain every few seconds, parse raw blocks, filter transactions, and pray you don't miss anything. It's expensive, fragile, and scales poorly.

There's a better way. Blockchain notifications event-driven webhooks that push updates to your server the moment something happens on-chain.

In this guide, I'll walk through how blockchain notifications work, why they matter, and how to set them up using Tatum's Notification API (v4) with practical code examples you can run today.

What Are Blockchain Notifications?

A blockchain notification is a webhook, an HTTP POST request fired to your server when a specific on-chain event occurs. That event could be:

  • An incoming or outgoing token transfer
  • A native currency transaction (ETH, MATIC, BTC, etc.)
  • An NFT mint or transfer
  • A smart contract interaction
  • A failed transaction

Instead of your application constantly asking "did anything happen yet?", the blockchain tells you.

Think of it like the difference between refreshing your inbox every 10 seconds vs. getting a push notification when a new email arrives.

Why Not Just Parse Blocks Manually?

You could run your own node, subscribe to new blocks, decode every transaction, and maintain state yourself. Some teams do. But here's what that actually looks like:

  • Infrastructure overhead: You need a full or archive node, which requires significant compute and storage.
  • Parsing complexity: Raw block data includes every transaction on the network. You need to filter, decode ABI data, handle internal calls, and track token standards (ERC-20, ERC-721, ERC-1155).
  • Reliability: Miss a block during a restart? You now have a data gap. You need reorg detection, retry logic, and checkpoint management.
  • Multi-chain support: Every chain has its own RPC quirks. Supporting Ethereum, Polygon, Tron, and Bitcoin means four different parsing implementations.

Notifications abstract all of this away. You subscribe once, and the platform handles the monitoring, parsing, and delivery.

How It Works: The 4-Step Flow

Here's the lifecycle of a blockchain notification.

1. Subscribe

You tell the notification service: "Watch this address on this chain, and POST to my webhook URL when something happens."

curl -X POST \
  'https://api.tatum.io/v4/subscription?type=mainnet' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "ADDRESS_EVENT",
    "attr": {
      "address": "0xF64E82131BE01618487Da5142fc9d289cbb60E9d",
      "chain": "ethereum-mainnet",
      "url": "https://your-app.com/webhook"
    }
  }'
{
  "id": "5e68c66581f2ee32bc354087"
}

The response returns an object with a id field, a string identifier for the subscription. Store it.

2. Monitor

The platform begins watching the blockchain. For EVM chains, this means tracking every block for transactions involving your address as sender, receiver, or through internal contract calls. You don't need to do anything here.

3. Receive

When a matching event is detected, your webhook receives an HTTP POST with a JSON payload:

{
  "address": "0xF64E82131BE01618487Da5142fc9d289cbb60E9d",
  "amount": "0.001",
  "asset": "ETH",
  "blockNumber": 2913059,
  "counterAddress": "0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990",
  "txId": "0x062d236ccc044f68194a04008e98c3823271dc26…",
  "type": "native",
  "chain": "ethereum-mainnet",
  "subscriptionType": "ADDRESS_EVENT"
}

Key fields to note:

  • type tells you the asset category: native, token (ERC-20), erc721, erc1155, internal, or fee
  • address and counterAddress Identify the parties involved (more on this below)
  • txId lets you look up the full transaction on-chain

4. Unsubscribe

When you no longer need monitoring, delete the subscription using the ID you stored earlier:

curl -X DELETE \
  'https://api.tatum.io/v4/subscription/YOUR_SUBSCRIPTION_ID' \
  -H 'Content-Type: application/json'

Understanding address vs counterAddress

This trips up a lot of developers, so let's be explicit.

For native asset transfers (ETH, MATIC, etc.):

  • counterAddress = the sender (from)
  • address = the receiver (to)

For token transfers (ERC-20, ERC-721, etc.):

  • address = the sender (from)
  • counterAddress = the receiver (to)

Yes, they're reversed depending on the asset type. Here's a quick reference:

Native (ETH): address = Receiver (To) · counterAddress = Sender (From)
Token (ERC-20): address = Sender (From) · counterAddress = Receiver (To)
NFT (ERC-721): address = Sender (From) · counterAddress = Receiver (To)

Tron exception: TRC-10 and TRC-20 tokens follow the native asset pattern counterAddress is the sender, address is the receiver.

Supported Chains

Notifications work across a wide range of blockchains. Here are the major ones supported for ADDRESS_EVENT (the broadest type).

  • EVM Chains: Ethereum, Polygon, BNB Smart Chain, Arbitrum One, Avalanche, Base, Optimism, Fantom, Cronos, Celo, Chiliz, Klaytn/Kaia, Flare, Berachain, Monad, Unichain, MocaChain.
  • UTXO Chains: Bitcoin, Litecoin, Dogecoin, Bitcoin Cash.
  • Other: Solana, Tron, XRP Ledger, Tezos.

Each chain is referenced using its network identifier (e.g. ethereum-mainnet, polygon-amoy, bitcoin-testnet, solana-devnet). Both mainnet and testnet variants are available for most chains.

Not all subscription types are available on every chain. For example, internal transaction subscriptions (INCOMING_INTERNAL_TX, OUTGOING_INTERNAL_TX) are only available on EVM chains.

Going Beyond Basic Monitoring

Subscription Types

ADDRESS_EVENT is the catch-all, but you can be more specific:

  • INCOMING_NATIVE_TX Incoming native currency (ETH, MATIC, SOL, etc.)
  • OUTGOING_NATIVE_TX Outgoing native currency
  • INCOMING_FUNGIBLE_TX Incoming ERC-20 / fungible tokens
  • OUTGOING_FUNGIBLE_TX Outgoing ERC-20 / fungible tokens
  • INCOMING_NFT_TX Incoming ERC-721 NFTs
  • OUTGOING_NFT_TX Outgoing ERC-721 NFTs
  • INCOMING_MULTITOKEN_TX Incoming ERC-1155 multi-tokens
  • OUTGOING_MULTITOKEN_TX Outgoing ERC-1155 multi-tokens
  • INCOMING_INTERNAL_TX Incoming internal (trace-level) transfers
  • OUTGOING_INTERNAL_TX Outgoing internal (trace-level) transfers
  • PAID_FEE Gas fee payments
  • OUTGOING_FAILED_TX Failed outgoing transactions
  • FAILED_TXS_PER_BLOCK All failed transactions in a block
  • CONTRACT_ADDRESS_LOG_EVENT Smart contract log events

Using specific types reduces noise and is more efficient than filtering a broad ADDRESS_EVENT subscription.

Each subscription must be unique within your API key. Uniqueness depends on the type:

  • Address subscriptions uniquely by type + chain + address + url
  • Contract log subscriptions are unique by type + chain + contractAddress + event + url
  • Block subscriptions unique by type + chain + url

Conditions: Filter Before Delivery

Instead of receiving every event and filtering in your application code, you can define conditions at the subscription level. Only events matching all conditions trigger a webhook.

Example: Only notify for transfers above 1 ETH

{
  "type": "ADDRESS_EVENT",
  "attr": {
    "chain": "ethereum-mainnet",
    "address": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
    "url": "https://your-app.com/webhook",
    "conditions": [{ "field": "value", "operator": ">", "value": "1000000000000000000" }]
  }
}

Example: Only notify for incoming USDT above 1,000 USDT

{
  "type": "INCOMING_FUNGIBLE_TX",
  "attr": {
    "chain": "ethereum-mainnet",
    "address": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
    "url": "https://your-app.com/webhook",
    "conditions": [
      {
        "field": "contractAddress",
        "operator": "==",
        "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
      },
      { "field": "value", "operator": ">=", "value": "1000000000" }
    ]
  }
}

Available filter fields: value, contractAddress, from, to, tokenId, tokenMetadata.type, tokenMetadata.symbol.

Important: Amount values are always in the chain's smallest unit. 1 ETH = "1000000000000000000" (18 decimals). 1 USDT = "1000000" (6 decimals). Always pass as a string.

Finality Levels

You can control when a notification fires relative to block confirmation:

  • confirmed Fires as soon as the transaction is confirmed. Optimised for speed.
  • final Waits for full block confirmation depth. Optimised for settlement confidence.

Use confirmed for real-time UX updates. Use final for payment processing or treasury operations where you can't afford a reorg reversal.

Templates: Controlling the Webhook Payload

By default, webhooks use a legacy payload format. But Tatum provides a built-in enriched template that returns structured, human-readable fields with token metadata, and it's the recommended default.

You choose a template by passing templateId when creating a subscription. Available options:

  • enriched Structured payload with token metadata (recommended)
  • enriched_with_raw_data Enriched data plus raw transaction data
  • legacy Original flat format (default if omitted)
  • Custom ID Your own template (see below)

Example: Subscribe with the enriched template

curl -X POST \
  'https://api.tatum.io/v4/subscription?type=mainnet' \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "ADDRESS_EVENT",
    "attr": {
      "address": "0xF64E82131BE01618487Da5142fc9d289cbb60E9d",
      "chain": "ethereum-mainnet",
      "url": "https://your-app.com/webhook"
    },
    "templateId": "enriched"
  }'

Enriched payload example:

{
  "data": {
    "kind": "transfer",
    "blockHash": "0x1234567890abcdef…",
    "blockNumber": 18500000,
    "blockTimestamp": 1699123456,
    "txId": "0xabcdef1234567890…",
    "currency": "ETH",
    "txTimestamp": 1699123456,
    "from": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
    "to": "0x8ba1f109551bD432803012645Ac136Ddd64DBA72",
    "value": "1000000000000000000",
    "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "tokenId": "12345",
    "additionalData": { "gasUsed": "21000", "gasPrice": "20000000000" },
    "tokenMetadata": {
      "type": "nft",
      "decimals": 0,
      "symbol": "NFT",
      "name": "My NFT",
      "uri": "https://api.example.com/metadata/12345"
    },
    "subscriptionId": "64f1a2b3c4d5e6f7g8h9i0j1",
    "subscriptionType": "ADDRESS_EVENT"
  }
}

Notice how the enriched payload gives you from/to directly (no more address/counterAddress confusion), includes tokenMetadata with symbol and decimals, and provides additionalData with gas details.

Creating a custom template:

If you need full control over the payload shape, create your own:

curl -X POST \
  'https://api.tatum.io/v4/subscription/template' \
  -H 'Content-Type: application/json' \
  -H 'x-api-key: YOUR_API_KEY' \
  -d '{
    "format": "json",
    "keys": {
      "amount": "value",
      "sentFrom": "from",
      "sentTo": "to"
    }
  }'

This returns a template ID you can then reference in any subscription.

Securing Your Webhooks

A webhook endpoint is a publicly accessible URL. Anyone who discovers it could send fake payloads. Two defenses:

HMAC Verification

When HMAC is enabled, every webhook includes an x-payload-hash header containing a cryptographic digest. Your server reconstructs the hash using your shared secret and compares. If they match:

  • The payload wasn't tampered with in transit
  • The request was sent by Tatum, not an attacker

This is the recommended approach.

IP Whitelisting

Tatum publishes its IP ranges at ips.tatum.com/ips.json. You can whitelist these in your WAF. This works, but HMAC is more robust since IP ranges can change.

Always Use HTTPS

This should go without saying, but always use HTTPS for your webhook URLs to encrypt payloads in transit.

What Happens When Your Server Is Down?

Webhooks fail. Servers restart. Networks hiccup. Tatum handles this with automatic retries:

  • Free plan: 3 retry attempts over a ~2 minute window
  • Pay-As-You-Go: 10 retry attempts over a ~24 hour window
  • Business: 10 retry attempts over a ~24 hour window

The retry interval follows an exponential backoff formula:

delay (seconds) = 15 × 2.9516^(retryCount - 1)

Which produces intervals of roughly: 15s, 44s, 2min, 6min, 19min, 56min, 2hr, 8hr, 24hr.

You can also query past webhook deliveries, including failures, to audit what was sent and what your server responded:

curl -X GET \
  'https://api.tatum.io/v4/subscription/webhook?pageSize=10'

Each record includes the HTTP response code, response body, retry count, and whether the delivery ultimately failed.

Practical Architecture

Here's how this typically fits into a production system:

Blockchain (ETH, BTC, MATIC…) → Tatum Notification Service → Your Webhook Endpoint (POST /webhook) → Message Queue (SQS, Redis, RabbitMQ) → Worker Service (process event, update DB, notify user)

Best practice: Your webhook endpoint should do minimal work to validate the HMAC, enqueue the payload, and return 200 OK immediately. Heavy processing (database writes, user notifications, business logic) happens asynchronously in a worker. This keeps your webhook responsive and prevents timeouts that trigger unnecessary retries.

Quick Start Checklist

  • Get an API key from the Tatum Dashboard
  • Set up a webhook endpoint using webhook.site for testing
  • Create a subscription using the REST API
  • Verify delivery by sending a test transaction to your monitored address
  • Enable HMAC for production security
  • Add conditions to filter noise
  • Implement retry handling return 200 quickly, process asynchronously

Wrapping Up

Blockchain notifications replace the fragile poll-parse-filter pattern with a clean, event-driven architecture. You define what you care about, which address, which chain, which event type, under what conditions, and the platform handles the rest.

The result is less infrastructure, fewer missed events, and an application that reacts to the blockchain in real time instead of lagging behind it.

If you're building anything that needs to know when something happens, on-chain payment confirmations, wallet activity monitoring, NFT tracking, and DeFi position management notifications are the foundation to build on.

Building with blockchain notifications? I'd love to hear about your use case. Drop a comment or connect with me.