Skip to main content
Zerion webhooks push transaction data to your server the moment activity is detected on a watched wallet — no polling required. You create a subscription, add wallet addresses, and Zerion sends a POST request to your callback URL for every new transaction.
Webhooks are part of the Subscriptions to Transactions API. This page explains how the system works. For a hands-on walkthrough, see the Wallet Activity Alerts recipe.

How it works

1

Create a subscription

Call Create Subscription with a callback_url and a list of wallet addresses. Optionally filter by chain_ids to limit which chains you monitor.
2

Zerion monitors the wallets

Zerion watches all specified wallets across the selected chains (or all supported chains if none are specified).
3

Your server receives notifications

When a watched wallet sends or receives a transaction, Zerion sends a POST request to your callback URL with the full transaction payload.

Payload format

Every webhook notification is a POST request with a JSON body following the JSON:API structure. The top-level data object describes the notification, while the included array contains the full transaction details.
{
  "data": {
    "id": "notification-id",
    "type": "transaction_notification",
    "attributes": {
      "timestamp": "2024-07-31T00:17:36Z",
      "callback_url": "https://example.com/callback",
      "address": "0x42b9df65b219b3dd36ff330a4dd8f327a6ada990"
    },
    "relationships": {
      "subscription": {
        "type": "tx-subscriptions",
        "id": "87db77a6-17eb-4ca8-af0e-e43cbe9c83c6"
      }
    }
  },
  "included": [
    {
      "type": "transactions",
      "id": "52d994a173d755e99845e861d534a419",
      "attributes": {
        "operation_type": "send",
        "hash": "0xabc123...",
        "mined_at": "2024-07-31T00:17:35Z",
        "mined_at_block": 7490818,
        "sent_from": "0x42b9df65b219b3dd36ff330a4dd8f327a6ada990",
        "sent_to": "0x1234567890abcdef1234567890abcdef12345678",
        "status": "confirmed",
        "nonce": 250,
        "fee": {
          "fungible_info": { "symbol": "ETH", "name": "Ethereum" },
          "quantity": { "float": 0.00042, "int": "420000000000000", "decimals": 18 }
        },
        "transfers": [
          {
            "direction": "out",
            "quantity": { "float": 0.5, "int": "500000000000000000", "decimals": 18 },
            "fungible_info": { "symbol": "ETH", "name": "Ethereum" }
          }
        ],
        "approvals": [],
        "application_metadata": null
      },
      "relationships": {
        "chain": { "type": "chains", "id": "ethereum" },
        "dapp": { "type": "dapps", "id": "" }
      }
    }
  ]
}

Key fields

FieldDescription
data.attributes.addressThe watched wallet that triggered this notification
data.relationships.subscription.idThe subscription this notification belongs to
included[].attributes.operation_typeTransaction type: send, receive, trade, execute, etc.
included[].attributes.statusconfirmed, failed, or pending
included[].attributes.transfersArray of token movements with direction, amount, and token info
included[].relationships.chain.idThe chain where the transaction occurred
Token prices are always null in webhook payloads. Prices are calculated asynchronously and are not available at the time of delivery. If you need prices, fetch them separately using the Fungibles API.

Signature verification

Every webhook request includes headers for verifying authenticity. Always verify signatures in production to ensure requests originate from Zerion.
HeaderDescription
X-TimestampISO 8601 timestamp of the request
X-SignatureBase64-encoded RSA-SHA256 signature
X-Certificate-URLURL to download the public certificate

Verification steps

  1. Concatenate the signing string: ${X-Timestamp}\n${request_body}\n
  2. Fetch the public certificate from the X-Certificate-URL header
  3. Verify the X-Signature against the signing string using RSA-PKCS1v15 with SHA-256
import base64
import requests
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def verify_webhook(timestamp, body, signature_b64, certificate_url):
    # Build the signing string
    signing_string = f"{timestamp}\n{body}\n"

    # Fetch and parse the certificate
    cert_pem = requests.get(certificate_url).content
    cert = load_pem_x509_certificate(cert_pem)
    public_key = cert.public_key()

    # Verify the signature
    signature = base64.b64decode(signature_b64)
    public_key.verify(
        signature,
        signing_string.encode(),
        padding.PKCS1v15(),
        hashes.SHA256()
    )
Cache the certificate after the first fetch to avoid an extra HTTP call on every webhook. If signature verification fails, re-fetch the certificate in case it has rotated.

Retry behavior

If your server fails to respond with a 2xx status code, Zerion retries delivery up to 3 times. After 3 failed attempts, the notification is dropped permanently. To minimize missed notifications:
  • Return a 200 response as quickly as possible — process the payload asynchronously
  • Keep your endpoint available with high uptime
  • Monitor your endpoint for errors and slow responses

Rollbacks

If a transaction is removed from the canonical chain (e.g., due to a chain reorganization), Zerion sends a second webhook for the same transaction with an additional deleted field:
{
  "data": {
    "type": "transaction_notification",
    "attributes": {
      "timestamp": "2024-07-31T00:18:12Z",
      "callback_url": "https://example.com/callback",
      "address": "0x42b9df65b219b3dd36ff330a4dd8f327a6ada990",
      "deleted": true
    }
  }
}
When deleted is true, the transaction has been rolled back and is no longer part of the canonical chain. Use the transaction hash to match it against the original notification and remove or mark it accordingly in your system.
A single transaction can trigger two webhooks: one on initial confirmation and one on rollback. Make sure your handler accounts for this rather than assuming one webhook per transaction.

Delivery guarantees

Zerion webhooks are best-effort:
  • Not guaranteed — if all 3 delivery attempts fail, the notification is dropped
  • Order is not guaranteed — notifications may arrive out of order relative to on-chain transaction ordering
  • Duplicates are possible — your server should handle the same notification arriving more than once
Design your webhook handler to be idempotent: use the transaction hash and chain to deduplicate, and don’t assume notifications arrive in chronological order.

Subscription limits

Free planPaid plan
Wallets per subscription5Unlimited
On the free plan, each subscription can monitor up to 5 wallets. On a paid plan, there is no limit — you can add as many wallets as you need. The API accepts up to 100 wallets per request, so for larger lists, batch your additions across multiple calls.

Testing webhooks

Use webhook.site to get a temporary callback URL for testing:
  1. Go to webhook.site and copy your unique URL
  2. Create a subscription with that URL as the callback_url
  3. Trigger a transaction on a watched wallet
  4. Inspect the payload and headers on webhook.site
If you want to test with your own URL or move to production, go to the Dashboard and click Support to request whitelisting for your callback URL.