Retries and Endpoint Protection
How GetBlock webhooks treat your endpoint's answers: the retry schedule, concurrency and rate limits, the circuit breaker, auto-pause, target URL rules, delivery source addresses and the delivery hist
Outcomes
Answers 2xx within 15 seconds
Delivered
Answers anything else — 3xx, any 4xx, 5xx
Failed attempt
Cannot be reached: a DNS, connection or TLS error, or the host resolves to a private or reserved address
Failed attempt
Does not connect within 5 seconds or does not answer within 15 seconds
Failed attempt
Every failed attempt counts the same towards the circuit breaker and auto-pause below. No status code is treated as permanent: a 4xx is retried like a 5xx.
Retry schedule
An event has 8 attempts. The wait before each next attempt:
Wait
5 s
15 s
30 s
1 min
5 min
15 min
30 min
The last attempt comes about 52 minutes after the first. If it fails too, the event is recorded as failed (visible in the delivery history with its last status or error) and is not sent again: there is no replay. An endpoint that is down for longer than that loses the events of that time.
Changes to a webhook — a new URL, a rotated secret, a pause — apply to the next event at once; retries already scheduled may use the previous settings for up to 60 seconds. A pause also stops matching at once, but a confirmed-phase delivery ("confirmed": true, or a tx_confirmation) of an event matched before the pause may still arrive for up to about 60 seconds after it.
Endpoint protection
These rules protect your endpoint and everyone else's; you may notice them.
Concurrency: at most 10 requests to one endpoint URL at a time. A request over the limit waits — up to 10 minutes in total, then it spends an attempt.
Request rate per host: at most 200 requests per second to one host, shared by all webhooks of all customers that point at that host. Waiting for the rate is not a failure — up to 10 minutes in total, then it spends an attempt.
Circuit breaker: more than 90 % failed attempts over 2 minutes, with at least 20 attempts, stops requests to that endpoint URL for 5 minutes; then one probe request decides whether it reopens. While it is open, attempts are spent without a request. They are not billed, but they count in
failed_totalof the webhook's stats.Auto-pause: when every attempt of a webhook has failed for at least 30 minutes, with at least 20 attempts, the webhook becomes
paused_auto(Paused automatically in the dashboard). Its queued events are dropped and recorded asauto_paused. It stays paused until you resume it: there is no automatic resume and no e-mail notification.Loss window: while a webhook is paused — by you, automatically, by plan or for balance — events are not matched at all. They are not queued and are never delivered later. The loss lasts until you resume.
A routine secret rotation never causes failures by itself (both secrets sign for 24 hours), so it cannot trigger auto-pause.
Test deliveries count like any other delivery for the circuit breaker and auto-pause.
Target URL rules
The URL is checked when you create a webhook and on every update that sends a target_url. Each rule has its own 400 code:
target_url_required
Empty
target_url_malformed
Not a URL, or an invalid host name
target_url_must_be_https
Any scheme but https
target_url_has_userinfo
user:pass@
target_url_has_fragment
#…
target_url_no_host
No host
target_url_internal_address
A literal IP that is loopback, private (RFC 1918, fc00::/7), link-local (incl. 169.254.169.254), multicast, unspecified, 100.64.0.0/10, 192.0.0.0/24, 198.18.0.0/15, 240.0.0.0/4 (incl. broadcast), or documentation space (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32)
target_url_reserved_host
localhost, .local, .internal, .test, .invalid, .example, .home.arpa, .onion (and names under them), any single-label name
target_url_not_public_domain
A name without a registrable label above an ICANN public suffix (example.notatld, co.uk), or a suffix itself (github.io)
Names under a hosting suffix are accepted:
foo.github.io,app.herokuapp.com.The URL is stored in canonical form: scheme and host in lower case, a trailing dot removed, an international host name in punycode. Compare against what the API returns.
No DNS lookup happens when you save the URL. At delivery time the host is resolved, and a private or reserved address is refused — that attempt fails.
Delivery source addresses
GetBlock does not publish a list of delivery IP addresses. Deliveries come from GetBlock infrastructure in the EU, and the addresses may change without notice. Verify the signature on every request — it is the only reliable check of origin (see Verifying Signatures). An IP allowlist on your side is not recommended: it can start rejecting legitimate deliveries at any time.
Delivery history and stats
The Deliveries and Stats tabs of a webhook in the dashboard show the same data as these two endpoints.
GET /api/v1/webhooks/{id}/deliveries lists failed attempts only, newest first. Under a long failure it keeps only a sample: about one row a second per webhook after the first 20. Query parameters:
from,to— RFC 3339 times;fromis inclusive,toexclusive;status—retrying(will be retried) orfailed_terminal(given up), repeatable or comma-separated.deliveredandreplayedare accepted but never match: successful attempts, test deliveries included, are not listed, and nothing is replayed;limit— page size, 100 by default, at most 999;cursor— thenext_cursorof the previous page.
http_status is null when no response was received; error, when present, says why the attempt failed; delivered_at is when the attempt was made.
GET /api/v1/webhooks/{id}/stats has the exact counts:
delivered_total— successful attempts;failed_total— failed attempts, each retry counted, including attempts spent without a request while the circuit breaker is open;rate_limited_total— events dropped above your plan's events-per-second limit (see Plan limits).
delivered_total and failed_total include test deliveries. The answer also has last_delivered_at, updated_at and, when available, rates — delivered and failed counts over the last minute, 5 minutes and hour (window_60s, window_300s, window_3600s); rates is null and rates_available is false when they are temporarily unavailable. Totals update with a delay of a few seconds.
Last updated
Was this helpful?