From ec292584c7de298fd89a184b29dda9405119ec92 Mon Sep 17 00:00:00 2001 From: Brian Luong Date: Thu, 2 Jul 2026 14:32:54 -0700 Subject: [PATCH 1/2] Document GraphQL webhook payload truncation and 10 MB size limit Co-Authored-By: Claude Fable 5 --- .../custom-webhooks-faq.mdx | 14 ++++ .../webhooks/webhook-types/custom-webhook.mdx | 65 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx b/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx index 977b05894..d01fa18a8 100644 --- a/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx +++ b/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx @@ -77,6 +77,20 @@ If you're seeing 5xx error codes when creating webhooks, please ensure that you While logs and transactions can be self-referential entities, we only allow for a single-layer nested data structure to bound GraphQL queries. This helps us to to maintain a tidy API surface area. +## Why does my webhook payload have `truncated: true`? + +Custom Webhook payloads are capped at 10 MB. If your query's result for a block exceeds the limit, we still deliver the webhook, but with the result truncated to fit: list fields are cut to the elements that fit, and any list resolved after the limit is reached comes back as an empty array. The payload stays structurally valid JSON and includes three extra fields inside `event`: + +* `truncated` — `true` when the result is incomplete +* `truncatedFields` — the query field paths whose lists lost elements, for example `["block.logs", "block.logs.transaction.logs"]` +* `truncationReason` — a message naming the size limit that was exceeded + +Truncated payloads are always delivered, even with `skip_empty_messages` enabled, so an empty list in a truncated payload means the data was cut, not that the block had no matching activity. + +To stop receiving truncated payloads, narrow your GraphQL query so the full result fits under the limit — `truncatedFields` shows exactly which selections to trim. The most common cause is nesting `transaction { logs }` under `block.logs`, which re-embeds every matched log's full transaction log list and blows up the payload on log-dense blocks. + +See [Payload size limit](/docs/reference/custom-webhook#payload-size-limit) for a full example response. + ## How do I use the topic filter? * Currently, our log/event filter leverages the same semantics as a traditional topic filter for an *eth\_getLogs* RPC call. If you're not familiar with how topic filters work for *eth\_getLogs*, here's [an article](/docs/deep-dive-into-eth_getlogs) to get you up to speed. diff --git a/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx b/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx index d8b772773..1ab6a575a 100644 --- a/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx +++ b/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx @@ -115,6 +115,71 @@ Below you can find descriptions for select response fields. Note that this is no | `block` | Nested object under each block | `N/A` | | `sequenceNumber` | An incrementing integer used for client-side re-org handling. | `10000000000578619000` | +# Payload size limit + +Custom Webhook payloads are capped at **10 MB**. If your GraphQL query's result for a block exceeds this limit, we still deliver the webhook, but with the result truncated to fit: + +* List fields (such as `logs`) are cut to the elements that fit within the limit. +* Once the limit is reached, every list resolved after that point — including nested lists like a log's `transaction.logs` and scalar lists like `topics` — is returned as an empty array (`[]`). +* Non-list fields are never altered, so every object present in a truncated payload is complete, and the payload is always structurally valid JSON. + +A truncated payload carries three extra fields inside `event` so you can tell an incomplete result apart from a genuinely small block. All three are omitted when the payload is complete, so untruncated payloads are unchanged. + +| Field | Description | Value | +| ------------------ | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| `truncated` | `true` when the result was truncated to fit the size limit. | `true` | +| `truncatedFields` | The query field paths whose lists lost elements — exactly which parts of `data` are incomplete. | `["block.logs", "block.logs.transaction.logs"]` | +| `truncationReason` | A human-readable explanation naming the size limit. | `Result exceeded the 10485760-byte size limit and was truncated` | + + + ```shell Truncated Custom Webhooks Response + { + "webhookId": "wh_a55wfsvq5h8n8u3z", + "id": "whevt_ttg388dp5vjcnahd", + "createdAt": "2024-01-23T07:51:07.945953790Z", + "type": "GRAPHQL", + "event": { + "data": { + "block": { + "logs": [ + { + "transaction": { + "hash": "0x8a179038d909c1906fddf0a2d38e4d3ef76eba30c1c26f758451e3b69a64f5a6", + "index": 105, + "from": { + "address": "0xeeda051ab883d52923bc0951dd2edcbc7c0da597" + }, + "to": { + "address": "0xdafce4acc2703a24f29d1321adaadf5768f54642" + }, + "logs": [], + "type": 2, + "status": 1 + } + } + ] + } + }, + "sequenceNumber": "10000000000578619000", + "truncated": true, + "truncatedFields": [ + "block.logs", + "block.logs.transaction.logs" + ], + "truncationReason": "Result exceeded the 10485760-byte size limit and was truncated" + } + } + ``` + + +In this example, `block.logs` was cut to the entries that fit and each remaining log's nested `transaction.logs` came back empty — the block had more matching data than the payload shows. + + + Truncated payloads are always delivered, even if `skip_empty_messages` is enabled. An empty list in a truncated payload means the data was cut to fit the size limit, not that the block had no matching activity. + + +If you receive truncated payloads, narrow your query so the full result fits under the limit — `truncatedFields` shows exactly which selections to trim. The most common cause is re-embedding each matched log's full transaction log list, for example `block { logs { transaction { logs } } }`, which multiplies the payload size on log-dense blocks. + # How to Set Up Custom Webhooks 1. Navigate to the [Webhooks page](https://dashboard.alchemy.com/apps/latest/webhooks) in your Alchemy Dashboard. From ac16822d08bad3518170db4fc9045c18e9882760 Mon Sep 17 00:00:00 2001 From: Brian Luong Date: Thu, 2 Jul 2026 14:35:28 -0700 Subject: [PATCH 2/2] Trim truncation docs to a concise FAQ entry per review feedback Co-Authored-By: Claude Fable 5 --- .../custom-webhooks-faq.mdx | 8 +-- .../webhooks/webhook-types/custom-webhook.mdx | 65 ------------------- 2 files changed, 2 insertions(+), 71 deletions(-) diff --git a/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx b/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx index d01fa18a8..f42f1db4f 100644 --- a/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx +++ b/content/api-reference/data/webhooks/custom-webhooks-quickstart/custom-webhooks-faq.mdx @@ -79,17 +79,13 @@ While logs and transactions can be self-referential entities, we only allow for ## Why does my webhook payload have `truncated: true`? -Custom Webhook payloads are capped at 10 MB. If your query's result for a block exceeds the limit, we still deliver the webhook, but with the result truncated to fit: list fields are cut to the elements that fit, and any list resolved after the limit is reached comes back as an empty array. The payload stays structurally valid JSON and includes three extra fields inside `event`: +Custom Webhook payloads are capped at 10 MB. If a block's query result exceeds the limit, we still deliver the webhook but truncate the result: list fields are cut to the elements that fit, and lists resolved after that come back empty. Truncated payloads include three extra fields inside `event`: * `truncated` — `true` when the result is incomplete * `truncatedFields` — the query field paths whose lists lost elements, for example `["block.logs", "block.logs.transaction.logs"]` * `truncationReason` — a message naming the size limit that was exceeded -Truncated payloads are always delivered, even with `skip_empty_messages` enabled, so an empty list in a truncated payload means the data was cut, not that the block had no matching activity. - -To stop receiving truncated payloads, narrow your GraphQL query so the full result fits under the limit — `truncatedFields` shows exactly which selections to trim. The most common cause is nesting `transaction { logs }` under `block.logs`, which re-embeds every matched log's full transaction log list and blows up the payload on log-dense blocks. - -See [Payload size limit](/docs/reference/custom-webhook#payload-size-limit) for a full example response. +To stop receiving truncated payloads, narrow your query so the full result fits under the limit — `truncatedFields` shows exactly which selections to trim. The most common cause is nesting `transaction { logs }` under `block.logs`. ## How do I use the topic filter? diff --git a/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx b/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx index 1ab6a575a..d8b772773 100644 --- a/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx +++ b/content/api-reference/data/webhooks/webhook-types/custom-webhook.mdx @@ -115,71 +115,6 @@ Below you can find descriptions for select response fields. Note that this is no | `block` | Nested object under each block | `N/A` | | `sequenceNumber` | An incrementing integer used for client-side re-org handling. | `10000000000578619000` | -# Payload size limit - -Custom Webhook payloads are capped at **10 MB**. If your GraphQL query's result for a block exceeds this limit, we still deliver the webhook, but with the result truncated to fit: - -* List fields (such as `logs`) are cut to the elements that fit within the limit. -* Once the limit is reached, every list resolved after that point — including nested lists like a log's `transaction.logs` and scalar lists like `topics` — is returned as an empty array (`[]`). -* Non-list fields are never altered, so every object present in a truncated payload is complete, and the payload is always structurally valid JSON. - -A truncated payload carries three extra fields inside `event` so you can tell an incomplete result apart from a genuinely small block. All three are omitted when the payload is complete, so untruncated payloads are unchanged. - -| Field | Description | Value | -| ------------------ | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| `truncated` | `true` when the result was truncated to fit the size limit. | `true` | -| `truncatedFields` | The query field paths whose lists lost elements — exactly which parts of `data` are incomplete. | `["block.logs", "block.logs.transaction.logs"]` | -| `truncationReason` | A human-readable explanation naming the size limit. | `Result exceeded the 10485760-byte size limit and was truncated` | - - - ```shell Truncated Custom Webhooks Response - { - "webhookId": "wh_a55wfsvq5h8n8u3z", - "id": "whevt_ttg388dp5vjcnahd", - "createdAt": "2024-01-23T07:51:07.945953790Z", - "type": "GRAPHQL", - "event": { - "data": { - "block": { - "logs": [ - { - "transaction": { - "hash": "0x8a179038d909c1906fddf0a2d38e4d3ef76eba30c1c26f758451e3b69a64f5a6", - "index": 105, - "from": { - "address": "0xeeda051ab883d52923bc0951dd2edcbc7c0da597" - }, - "to": { - "address": "0xdafce4acc2703a24f29d1321adaadf5768f54642" - }, - "logs": [], - "type": 2, - "status": 1 - } - } - ] - } - }, - "sequenceNumber": "10000000000578619000", - "truncated": true, - "truncatedFields": [ - "block.logs", - "block.logs.transaction.logs" - ], - "truncationReason": "Result exceeded the 10485760-byte size limit and was truncated" - } - } - ``` - - -In this example, `block.logs` was cut to the entries that fit and each remaining log's nested `transaction.logs` came back empty — the block had more matching data than the payload shows. - - - Truncated payloads are always delivered, even if `skip_empty_messages` is enabled. An empty list in a truncated payload means the data was cut to fit the size limit, not that the block had no matching activity. - - -If you receive truncated payloads, narrow your query so the full result fits under the limit — `truncatedFields` shows exactly which selections to trim. The most common cause is re-embedding each matched log's full transaction log list, for example `block { logs { transaction { logs } } }`, which multiplies the payload size on log-dense blocks. - # How to Set Up Custom Webhooks 1. Navigate to the [Webhooks page](https://dashboard.alchemy.com/apps/latest/webhooks) in your Alchemy Dashboard.