Delivery, retries and ordering
When Gauge retries, when it gives up, why you must deduplicate, and why events can arrive out of order.
Success and failure
A delivery succeeds when your endpoint answers any 2xx status within 10 seconds. Everything
else is a failure and is retried:
- any other status, including
3xx: Gauge never follows redirects, because the URL you registered is the one that was checked; - no answer within 10 seconds;
- a connection, DNS or TLS error.
Each endpoint's delivery log in Developers → Webhooks shows every attempt, with the status or the reason it failed ("Timed out after 10 s", "HTTP 503") and an excerpt of your response.
Retries
A failed delivery is retried on this schedule, then marked dead:
| Attempt | When |
|---|---|
| 1 | Immediately |
| 2 | 30 seconds after attempt 1 failed |
| 3 | 5 minutes later |
| 4 | 1 hour later |
| 5 | 6 hours later |
| Then dead: no more automatic attempts |
So an event survives a little over seven hours of downtime on its own. For a longer outage, replay the dead deliveries once you are back.
Endpoints that keep failing are switched off
An endpoint whose deliveries have all failed for 3 days in a row (no success since its first
failure) is disabled automatically: it gets enabled: false and
disabledReason: "consecutive_failures", and the member who owns it is told in the app and by
email. One successful delivery at any point resets the count.
While it is off, nothing is delivered to it. Fix the receiver, then switch it back on in the
console or with PATCH /v2/webhook-endpoints/{id} { "enabled": true } (which clears
disabledReason), and replay what you missed: events are kept for
30 days.
Headers
Every delivery is a POST with Content-Type: application/json and:
| Header | |
|---|---|
Gauge-Signature | t=<epoch ms>,v1=<hex>. See Verify signatures. |
Gauge-Event-Id | The event id, the same as the body's id. Dedupe on it. |
Gauge-Event-Type | The event name, e.g. message.received. |
Gauge-Delivery-Attempt | 1 on the first try, then 2, 3, … across retries and redeliveries. |
Gauge-Webhook-Id | The endpoint's id. |
x-pulse-signature, x-pulse-timestamp, x-pulse-event | Legacy copies of the signature, t and event name, for receivers written before API v2. |
Size
A body is at most 64 KB. A longer message's text is cut, with truncated: true and a
fetchUrl to read it in full; see Large messages.
Deduplicate on the event id
The same event can reach you more than once: a retry after your server processed the event but
timed out answering, a manual resend, or a replay. Every copy carries the same id.
Record the ids you have processed and skip any you have seen. Keep them for at least a few days, longer than the retry schedule.
-- One way: a unique key, and treat a conflict as "already handled".
CREATE TABLE gauge_events_seen (id text PRIMARY KEY, received_at timestamptz NOT NULL DEFAULT now());
INSERT INTO gauge_events_seen (id) VALUES ($1) ON CONFLICT (id) DO NOTHING RETURNING id;
-- No row returned → a duplicate. Answer 2xx and do nothing.Ordering
Deliveries are not guaranteed to arrive in the order the events happened. Events are sent in parallel, and a failed delivery is retried later while newer ones go through.
- Use
occurredAtto order events about the same thing, and ignore an update older than what you already have. - For anything where the latest state matters, treat the event as a signal and fetch the current
state from the API by
entityId.
Answer fast
Your endpoint has 10 seconds, and Gauge waits for each answer. Verify the signature, store the
event (or put it on a queue), answer 2xx, then do the slow work. An endpoint that does its work
inline risks timeouts, and every timeout becomes a retry.
Security
- Use an
httpsURL: deliveries carry your customers' data. Only addresses on the public internet are accepted; private, loopback and link-local addresses (and cloud metadata endpoints) are refused, both when you save the endpoint and again when Gauge connects. - Verify the signature on every delivery.
- Keep the signing secret on your server, and rotate it if it may have leaked.