Webhooks FAQ

Frequently asked questions about the Notify API

What does the capacity limit error mean?

The capacity limit error means you've exceeded your total monthly compute units. To upgrade your capacity limits go to your Alchemy dashboard. Below is an example capacity limit error.

{
  "webhookId": "wh_127lzhz0dfwwimwe",
  "id": "whevt_55i2al4sc2ivp1m8",
  "createdAt": "2022-12-05T23:43:16.379Z",
  "type": "ADDRESS_ACTIVITY",
  "event": {
    "error": "Monthly capacity limit exceeded. Visit https://dashboard.alchemy.com/settings/billing to upgrade your scaling policy for continued service."
  }
}

How do I validate the webhook event?

Every outbound request contains a hashed authentication signature in the header X-Alchemy-Signature. It's computed by concatenating your signing key and request body. Then generates a hash using the HMAC SHA256 hash algorithm.

To verify the signature came from Alchemy, you generate the HMAC SHA256 hash and compare it with the signature received.

Example Request Header

POST /yourWebhookServer/push HTTP/1.1
Content-Type: application/json;
X-Alchemy-Signature: your-hashed-signature

Example Signature Validation

import * as crypto from "crypto";

function isValidSignatureForStringBody(
    body: string, // must be raw string body, not json transformed version of the body
    signature: string, // your "X-Alchemy-Signature" from header
    signingKey: string, // taken from dashboard for specific webhook
  ): boolean {
    const hmac = crypto.createHmac("sha256", signingKey); // Create a HMAC SHA256 hash using the signing key
    hmac.update(body, "utf8"); // Update the token hash with the request body using utf8
    const digest = hmac.digest("hex");
    return signature === digest;
}
import hmac
import hashlib

def isValidSignatureForStringBody(body: str, signature: str, signing_key: str)->bool:
    digest = hmac.new(
        bytes(signing_key, "utf-8"),
        msg=bytes(body, "utf-8"),
        digestmod=hashlib.sha256,
    ).hexdigest()
    
    return signature == digest
func isValidSignatureForStringBody(
  body []byte, // must be raw string body, not json transformed version of the body
  signature string, // your "X-Alchemy-Signature" from header
  signingKey []byte, // taken from dashboard for specific webhook
) bool {
  h := hmac.New(sha256.New, signingKey)
  h.Write([]byte(body))
  digest := hex.EncodeToString(h.Sum(nil))
  return digest == signature
}
fn is_valid_signature_for_string_body(
    body: &[u8], // must be raw string body, not json transformed version of the body
    signature: &str, // your "X-Alchemy-Signature" from header
    signing_key: &str, // taken from dashboard for specific webhook
) -> Result<bool, Box<dyn std::error::Error>> {
    let signing_key_bytes: Vec<u8> = signing_key.bytes().collect();
    let mut mac = Hmac::<Sha256>::new_from_slice(&signing_key_bytes)?;
    mac.update(&body);
    let hex_decode_signature = hex::decode(signature)?;
    let verification = mac.verify_slice(&hex_decode_signature).is_ok();
    Ok(verification)
}

Where do I find my Alchemy auth token?

Find your Alchemy auth token in the upper-right corner of your Webhook dashboard by clicking the AUTH TOKEN button.

1600

Alchemy dashboard showing where to copy the Auth Token for the Webhooks API.

How many webhooks can I create?

These are the number of webhooks you can create per tier!

Tier# of Webhooks
Free5
Pay As You Go100
EnterpriseUnlimited

How many addresses can you add to a single webhook?

For Address Activity webhooks, we reliably support up to 100,000 addresses per single webhook.
For Custom webhooks, with variables, we support a maximum of 10M addresses.

Why am I missing transactions in the response?

Double check that you're parsing the response payload correctly. Remember, transactions are returned in a list. Transactions that are mined within the same block will be returned within the same activity list.

Why are address activity events for the same transaction split across multiple response?

You may notice activity entries from the same transaction being split across multiple requests when using the Address Activity webhooks. This is expected behavior because webhook streams are currently separated by block and type. This means that if there are external, internal and token transfers all within the same transaction that occurred in block 49, you will still receive 3 separate webhook payloads, one for each of them.

What are some best practices when using webhooks with a large number of addresses?

When working with large address lists, we suggest that users assign no more than 100,000 addresses to each webhook. If you find yourself using many addresses, spin up a new webhook to ensure that you have a reliable and scalable system.

How are reorgs handled?

When a chain reorganization occurs, block logs on the old chain will be emitted again with the property removed set to true.

Why am I getting 5xx errors repeatedly setting up a webhook?

You may be seeing repeated 5xx errors because of automatic webhook-retry-logic where requests are retried for non-200 response codes and upon failures to reach your server.

One common mistake we see is users not responding with 2xx status codes on a successful response which will trigger a retry. To resolve, set-up a webhook listener.

How does reaching my CU limit impact my webhook's activity?

If you hit your CU limit and don't have autoscaling activated, all your active webhooks will be set to the 'paused' state. This means they'll temporarily stop operating until you manually unpause them or acquire more CUs.

What happens when my webhooks are in a 'paused' state?

When your webhooks are 'paused', they won't operate or trigger any notifications. You'll need to either wait for more CUs or upgrade to unpause them.

Why won't my paused webhooks automatically unpause after I upgrade or when I acquire more CUs?

We've noticed that many of our users use up all their CUs quickly at the month's start. To ensure these webhooks don't consume resources without proving their value, we ask that you manually unpause them, even after an upgrade.

ReadMe