Skip to Content
WebhooksDelivery & Retries

Delivery & Retries

When watched addresses appear on-chain, Spectrum sends an HTTPS POST to your webhook URL. A single request carries every matching event from one block. This page covers the delivery payload and headers your server receives, how retries and dead-lettering work, the two behaviors your receiver must handle (idempotency and reorgs), and the delivery tools in the dashboard.

The delivery payload

Block-level fields live on the event envelope so they aren’t repeated per event. The event.activity[] array holds one entry per matched event, ordered by (txIndex, logIndex, subIndex).

{ "id": "whevt_a1b2c3d4e5f6", "type": "ADDRESS_ACTIVITY", "webhookId": "wh_4dbc2155930fa7054493aed0", "createdAt": "2026-05-08T12:34:56.789Z", "event": { "chain": "ethereum", "blockHash": "0x...", "blockNumber": 19000000, "blockTimestamp": 1700000000, "removed": false, "activity": [ { "category": "token", "fromAddress": "0x...", "toAddress": "0x...", "hash": "0x...", "txIndex": 12, "rawValue": "1000000", "value": "1", "asset": { "symbol": "USDC", "decimals": 6, "address": "0xa0b86991..." } } ] } }

Envelope fields

FieldDescription
idLogical batch id (whevt_…). Identifies this block-batch; stable across a reorg rollback for the same batch.
typeThe webhook type, e.g. ADDRESS_ACTIVITY.
webhookIdThe webhook that matched (wh_…). Matches the X-Spectrum-Webhook-Id header.
createdAtWhen the delivery was generated (RFC3339).
event.chainChain slug.
event.blockHash / event.blockNumberThe block these events came from.
event.blockTimestampUnix seconds.
event.removedtrue on a reorg rollback (see below), otherwise false.
event.activity[]One entry per matched event.

Activity entry fields

Each entry includes a self-describing core; additional fields appear depending on the event’s category. See Activity Categories for the fields each category carries.

FieldDescription
categoryexternal, internal, token, erc721, or erc1155.
fromAddress / toAddressLowercase hex addresses.
hashTransaction hash.
txIndexTransaction index within the block (decoded integer).
rawValueRaw base-unit amount as a string (no decimals applied).
valuerawValue ÷ 10^asset.decimals as an exact decimal string, or null if decimals aren’t resolved yet.
asset{ symbol, decimals, address? }. address is present for token categories.
tokenIdNFT token id (raw integer string), for erc721/erc1155.
logIndex, log, subIndex, typeTraceAddressCategory-specific fields, present for some categories. See Activity Categories.

Wire conventions

  • Addresses are lowercase hex everywhere (fromAddress, toAddress, asset.address).
  • rawValue is the exact base-unit integer as a string; value is the decimal-adjusted amount as a string to avoid float precision loss. value is null until token decimals are known.
  • blockNumber, txIndex, logIndex are decoded integers, re-encode to hex yourself if you need it.
  • tokenId is a raw integer string.

Delivery headers

X-Spectrum-Signature: t=<unix>,v1=<hmac-sha256-hex> X-Spectrum-Signature-Previous: t=<unix>,v1=<hmac> (only during a key rotation grace window) X-Spectrum-Attempt: 1 X-Spectrum-Event-Id: dq_a1b2c3d4e5f6a1b2c3d4 X-Spectrum-Webhook-Id: wh_4dbc2155930fa7054493aed0
HeaderPurpose
X-Spectrum-SignatureHMAC-SHA256 signature, verify every delivery. See Signing & Verification.
X-Spectrum-Signature-PreviousPresent only during a signing-key rotation grace window.
X-Spectrum-AttemptAttempt number for this delivery, starting at 1.
X-Spectrum-Event-IdThe idempotency key (dq_…). Stable across retries of the same delivery.
X-Spectrum-Webhook-IdThe webhook id, matches webhookId in the body.

Return a 2xx status once you’ve accepted the delivery. Any non-2xx (or a timeout) is treated as a failure and retried.

Retry policy

When your endpoint doesn’t return a 2xx, Spectrum retries with backoff. Both values are set per webhook from its settings.

SettingDefaultRangeBehavior
Max delivery attempts50 or a positive integerAttempts before the delivery is dead-lettered. 0 means retry forever.
Retry interval60s1s to 1 dayBase interval between retry attempts.
  • A delivery is considered successful when your server responds with a 2xx.
  • After the max attempts are exhausted, the delivery moves to the dead-letter queue. You can replay dead-lettered deliveries later.
  • Because retries can re-send a delivery, receivers must be idempotent (below).

Idempotency

Deliveries are at-least-once. The same delivery can arrive more than once, for example when your server accepts it but the connection drops before Spectrum records the 2xx, or after a worker restart.

Deduplicate on the X-Spectrum-Event-Id header. It is stable across every retry of the same delivery, so recording processed ids and dropping repeats makes your handler safe.

Don’t dedupe on the body’s id (whevt_…), that’s a logical batch id, not a per-delivery key, and it is intentionally reused for reorg rollbacks (below) and shared across a split batch.

Reorgs

When a block that was already delivered gets orphaned by a chain reorganization, Spectrum sends a rollback delivery with event.removed: true. The body carries the same batch id and the same activity[] as the original, so you can reverse-apply whatever state you derived from it.

{ "id": "whevt_a1b2c3d4e5f6", "type": "ADDRESS_ACTIVITY", "webhookId": "wh_4dbc2155930fa7054493aed0", "event": { "chain": "ethereum", "blockHash": "0x<orphaned>", "blockNumber": 19000000, "removed": true, "activity": [ /* same entries as the original delivery */ ] } }

How to handle it:

  • Treat event.removed === true as a reverse-apply signal, undo any state changes made from the original delivery.
  • The rollback arrives as a separate delivery, so its X-Spectrum-Event-Id header is a new value. Correlate the rollback with the original by matching the body id (and event.blockHash + activity[]), not the header id.
  • If you ignore the flag, your idempotency dedupe on the body id will treat the rollback as a duplicate and drop it, degraded but not unsafe.

Live reorg detection covers depths up to 64 blocks. Deeper or during-downtime reorgs are handled best-effort on listener recovery.

Large blocks (batch splitting)

If a single block produces more than 100 matching events for one webhook, Spectrum splits them across multiple POSTs. Each split has the same event.blockHash and event.blockNumber but a different body id. Correlate splits by block.

Sending a test delivery

From a webhook’s page in the dashboard, send a test delivery. Spectrum sends a real, signed POST to your configured URL, just like a live delivery, so you can confirm your receiver verifies the signature and returns a 2xx. The result appears in delivery history within a few seconds.

Delivery history

The dashboard records every delivery attempt for a webhook, so you can debug failures and confirm successes. Each attempt shows:

ColumnDescription
Attempted atWhen the attempt was made.
SuccessWhether your endpoint returned a 2xx.
Status codeThe HTTP status your endpoint returned. 0 means no HTTP response (timeout or network error).
Attempt numberWhich attempt this was, starting at 1.
DurationHow long the attempt took.
Category / chain / asset / matched addressesContext about the event delivered.
Error messageFailure detail, when the attempt failed.

Monitoring stats

The dashboard shows delivery stats per webhook and across your whole account, over a window you choose (e.g. last hour, 24 hours, 7 days):

  • Total attempts, successes, failures, and success rate.
  • Average and p95 delivery latency.
  • Breakdowns by category and by chain.

Pausing and resuming

Pause a webhook to stop deliveries without deleting it, useful during receiver maintenance. Toggle it back to active from the same place to resume delivery.

Replaying failed deliveries

When deliveries exhaust their retry policy they move to the dead-letter queue. After you’ve fixed your endpoint, replay them from the webhook’s page; they move back to pending and Spectrum retries them.

Replaying re-sends deliveries that previously failed. Make sure your receiver is idempotent (dedupe on X-Spectrum-Event-Id) before replaying. See Idempotency.

Audit history

Every change to a webhook (create, edit, delete, address changes, key rotation, unpause, and replay) is recorded in the dashboard’s audit log, with the action, who made it, and when. Use it for activity timelines and support investigations.