Gareth Wilson Gareth Wilson

Guide to Alchemy Webhooks: Features and Best Practices

Published


Alchemy Notify webhooks push blockchain activity to your backend: an address you're watching transacts, a transaction is mined or dropped, an NFT changes hands. If you're building on-chain-aware applications, webhooks are how you react in real time without polling nodes.

This guide covers how Alchemy webhooks work, the webhook types, how to verify the X-Alchemy-Signature (with the per-webhook signing key), the retry behavior, and the best practices for production.

What are Alchemy webhooks?

Alchemy Notify delivers HTTP POSTs to a URL you configure per webhook, each carrying blockchain event data scoped to a chain/network. Alchemy signs every delivery with an X-Alchemy-Signature header. The important nuance: the signing key is per-webhook, not per-app, so each webhook you create has its own key.

Alchemy webhook features

FeatureDetails
ConfigurationNotify dashboard or Notify API (scoped per chain/network)
Signature headerX-Alchemy-Signature
Signature schemeHMAC-SHA256 over the raw body, hex, keyed with the per-webhook signing key
Signing keyPer-webhook (not per-app); on the webhook's detail page or via the API
RetriesExponential backoff up to ~10 min (Free/PAYG) or ~1 hr (Enterprise)
IP allowlistOptional: 54.236.136.17, 34.237.24.169
SDKalchemy-sdk (NotifyNamespace) manages CRUD, but does not verify

Webhook types

Alchemy's webhook types are the on-the-wire enum values:

TypeFires when
ADDRESS_ACTIVITYA watched address has activity (transfers in/out)
MINED_TRANSACTIONA submitted transaction is mined
DROPPED_TRANSACTIONA submitted transaction is dropped
NFT_ACTIVITYAn NFT transfer occurs
NFT_METADATA_UPDATENFT metadata updates
GRAPHQLA Custom Webhook (GraphQL) fires on your defined query

Subscribe to the type you need, scoped to the right chain/network.

Setting up Alchemy webhooks

Create webhooks in the Notify dashboard or via the Notify API (which uses your app Auth Token, distinct from the signing key). Each webhook is scoped per chain/network. Copy the per-webhook signing key from the top-right of that webhook's detail page (or fetch it via the API); this is what you verify with. Optionally allowlist Alchemy's egress IPs (54.236.136.17, 34.237.24.169).

Securing Alchemy webhooks

Each delivery carries an X-Alchemy-Signature header: a hex-encoded HMAC-SHA256 of the raw request body, keyed with that webhook's signing key. Alchemy's docs stress this repeatedly: verify against the raw body, never a JSON-reparsed version.

const crypto = require("crypto");

// Per-WEBHOOK signing key (each webhook has its own)
const SIGNING_KEY = process.env.ALCHEMY_WEBHOOK_SIGNING_KEY;

function verify(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", SIGNING_KEY)
    .update(rawBody, "utf8")
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-alchemy-signature"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

The same check in Python:

import hashlib
import hmac
import os

SIGNING_KEY = os.environ["ALCHEMY_WEBHOOK_SIGNING_KEY"].encode()


def verify(raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(SIGNING_KEY, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

The alchemy-sdk (NotifyNamespace) manages webhook CRUD but doesn't verify signatures, so you implement this check yourself.

Alchemy webhook limitations and pain points

The signing key is per-webhook, not per-app

The Problem: Each webhook has its own signing key. Verifying with an app-wide key (or reusing one webhook's key for another) rejects legitimate deliveries.

Why It Happens: Alchemy scopes signing keys to individual webhooks.

Workarounds:

  • Store the correct signing key per webhook and select it by the receiving endpoint.

How Hookdeck Can Help: Hookdeck verifies each source with its own key at the edge, so your application receives pre-verified events without juggling per-webhook keys.

Raw-body sensitivity

The Problem: The HMAC is over the raw body. Any middleware that parses and re-serializes the JSON changes the bytes and breaks verification, an issue Alchemy's docs call out repeatedly.

Why It Happens: JSON reserialization doesn't preserve the exact bytes signed.

Workarounds:

  • Capture the raw body (express.raw, request.get_data()) and verify before parsing.

How Hookdeck Can Help: Hookdeck verifies against the bytes as received, so your app never has to preserve the raw body itself.

Retries and duplicates

The Problem: Alchemy retries with exponential backoff (up to ~10 min on Free/PAYG, ~1 hr on Enterprise), so the same event can arrive more than once. On-chain reorgs can also produce related events.

Why It Happens: At-least-once delivery favors eventual delivery.

Workarounds:

  • Dedupe on the event/webhook delivery ID, and make on-chain side effects idempotent and reorg-aware.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so retries don't double-process. See our guide to webhook idempotency.

High-volume address activity

The Problem: Watching busy addresses or broad NFT activity can generate high webhook volume that overwhelms a slow endpoint.

Why It Happens: On-chain activity is bursty and high-frequency.

Workarounds:

  • Acknowledge fast and process off a queue; scope webhooks narrowly to the addresses/networks you need.

How Hookdeck Can Help: Hookdeck absorbs bursts and forwards at a rate your endpoint can handle, so activity spikes don't cause failed deliveries.

Best practices

Verify with the per-webhook key over the raw body

Compute hex HMAC-SHA256 over the raw body with that webhook's signing key and compare in constant time.

Allowlist Alchemy's IPs

Optionally restrict the endpoint to Alchemy's egress IPs (54.236.136.17, 34.237.24.169) for defense in depth.

Acknowledge fast, process asynchronously

Return 200 immediately and defer work to a queue. See why to process webhooks asynchronously.

Dedupe and be reorg-aware

Dedupe on the delivery ID and make on-chain side effects idempotent so retries and reorgs don't double-process.

Make Alchemy webhooks production-ready

Hookdeck verifies X-Alchemy-Signature, deduplicates, absorbs bursts, and durably queues every event

Conclusion

Alchemy Notify webhooks push on-chain activity to your backend, verified with an X-Alchemy-Signature HMAC over the raw body, keyed with a per-webhook signing key. The details that matter are using the right per-webhook key, verifying against the raw bytes, and handling bursty high volume plus retries and reorgs.

Hookdeck verifies the signature, deduplicates, absorbs bursts, and durably queues every event at the edge, so your app only ever processes verified, unique events.

Get started with Hookdeck for free and handle Alchemy webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.