Webhooks

Retries and redelivery

Know what counts as a delivered webhook, how failures are retried, and how to redeliver an event from the log.

What counts as delivered

A delivery succeeds when your endpoint answers with any 2xx status within 10 seconds. Everything else is a failed attempt:

  • any 3xx, 4xx or 5xx status (redirects are not followed, so a 301 to a new address fails);
  • no answer within 10 seconds;
  • a connection or TLS error.

Answer first and do slow work afterwards. A receiver that processes the event before responding risks the 10-second limit and a retry of something it already handled.

The retry schedule

Each delivery gets four attempts: the first, then three retries.

AttemptWhen
1As soon as the event is emitted
210 seconds after attempt 1 fails
360 seconds after attempt 2 fails
45 minutes after attempt 3 fails

Every attempt of one delivery carries the same body and the same X-Transglot-Delivery id, with a fresh X-Transglot-Timestamp and X-Transglot-Signature. After the fourth failure, the delivery is marked failed and is not tried again.

When a delivery fails for good, transglot emails the workspace's developers and admins, A webhook endpoint is failing, naming the endpoint, the event and the last error. You get at most one of these a day per endpoint.

Missed events are not queued

Deliveries are not held for later. Once a delivery has failed, the event it carried reaches your endpoint only if you redeliver it from the log. The same is true for events that happen while an endpoint is paused or deleted: they are not sent afterwards.

Deliveries that are not attempted

SituationWhat happens
The endpoint was paused after the event was emittedThe delivery is marked failed without a request
The endpoint was deletedNothing more is sent, and its delivery log is deleted with it
The account's plan no longer includes webhooks, or the workspace cannot send data outNo delivery is created, and pending ones are marked failed without a request
The endpoint's host now resolves to a private or internal addressThe delivery is refused before sending, marked failed and not retried. transglot emails A webhook endpoint was refused

Endpoints must be publicly reachable HTTPS URLs; private and internal addresses are refused. The address is checked when the endpoint is created and again immediately before every attempt, so a host whose DNS later points inside a private network is still refused.

The delivery log

In the console, open Settings → Developer, find the endpoint under Webhooks and select Deliveries. Recent deliveries lists every attempt sequence for that endpoint, newest first:

ColumnShows
EventThe event name
Statuspending, delivering, succeeded or failed
CodeThe HTTP status your endpoint returned on the latest attempt
AttemptsHow many attempts have been made
Next retryWhen the next attempt is due, while retries remain
WhenWhen it was delivered, or when it was first dispatched

Filter by Delivery status to see only failures, and select Load more for older entries.

Redeliver an event

Select Redeliver on any entry in the log. transglot queues a new delivery of the same event to the same endpoint and confirms: "Redelivery queued. The new attempt appears at the top of this delivery log."

A redelivery:

  • carries byte-for-byte the same body as the original, including its original delivered_at;
  • gets a new X-Transglot-Delivery id, a fresh timestamp and a fresh signature;
  • follows the same retry schedule;
  • fails without a request if the endpoint is paused, so switch it back on first.

Redelivery is the quickest way to debug a receiver: fix it, then redeliver the failed entry instead of waiting for the event to happen again.

Handling duplicates and order

Design the receiver to tolerate the same event more than once and events out of order:

  • Duplicates. A retry repeats the same X-Transglot-Delivery id, so store the ids you have processed and skip repeats. A redelivery has a new id; when you redeliver on purpose, you usually want it processed.
  • Order. Deliveries are sent independently and can arrive in any order. Do not infer state from arrival order: read batch.status and batch.finished_at, or fetch the current state from the API.
  • Coalesced events. A count covers one window. Two windows of the same event are two separate deliveries.
Idempotent handler
const seen = new Set(); // use a database or cache with a TTL in production

export async function handle(deliveryId, payload) {
  if (seen.has(deliveryId)) return; // a retry of something already processed
  seen.add(deliveryId);

  if (payload.event === 'batch.completed') {
    await triggerDeploy(payload.project.slug, payload.batch.id); // your own deploy step
  }
}

On this page