Delivery history and retries

Available in the Enterprise and Pro plans. View plans

What this covers

The delivery log, retrying a delivery by hand, and diagnosing an endpoint that is not receiving. Requires the Webhooks feature and the manage-settings permission.

Each endpoint has its own log, reached from Property Settings → Webhooks.

What the log records

ColumnWhat it tells you
EventWhich event this was
StatusPending, delivered or failed
Response codeWhat your server answered — 200, 404, 500
Response bodyWhat your server actually said, or the connection error if it never answered
AttemptsHow many times it has been tried
Next retryWhen the next attempt is due, if any remain
Delivered atWhen it succeeded
PayloadExactly what was sent

The response body is the most useful column and the one people miss. When your server returns a 500, whatever it printed is captured here — often the actual error from your own code, which saves going through your logs.

The retry schedule

Four attempts: immediately, then after 1 minute, 10 minutes and 1 hour. A non-2xx response or a timeout counts as a failure. The whole window is a little over an hour.

After the fourth attempt the delivery is marked failed and left alone. It is not retried again automatically — the only route back is a manual retry.

Retrying by hand

Each delivery has a retry action. It resends the same payload and resets the attempt count, so a retried delivery gets the full four attempts again.

Use it after fixing whatever was wrong. Two cautions:

  • The payload is the original, not current state. Retrying a day-old reservation.updated replays what was true then — which may since have changed. For anything old, it is usually better to re-read the record through the API than to replay a stale event (link API reference).
  • Your endpoint must be idempotent. A retry of a delivery your server actually processed before failing to respond must not double up. That is what the delivery id is for (link Webhooks).

Diagnosing a failure

Start with the response code. It tells you whose problem it is.

What you seeUsually meansFix
404Wrong URL, or the route was removed or renamedCheck the URL, including its path and any trailing slash
401 / 403Your own app is refusing it — often signature verification failing, or authentication in front of the endpointSee Verifying signatures; webhook endpoints must not sit behind a login
405The route does not accept POSTAccept POST
500Your code threw. The response body normally shows whatRead the response body in the log
502 / 503Your server or a proxy in front of it was unavailableUsually resolves; the retries may cover it
A timeout or connection errorNothing answered within 30 seconds — server down, firewall, or your handler is too slowSee below
A certificate errorExpired, self-signed or incomplete HTTPS certificateFix the certificate chain; self-signed will not be accepted

The usual suspects, in order

  1. Your handler does the work before responding. The commonest real cause. Acknowledge with a 200 immediately and process afterwards — a handler that writes to a slow database before answering will time out under load and then receive the retry as well.
  2. Signature verification against a re-encoded body. Every delivery fails while your code looks right (link Verifying signatures).
  3. A firewall or security product blocking the request. Ask whoever runs your hosting; some web application firewalls block unfamiliar POST traffic by default.
  4. The endpoint is behind a login. Tabledoo cannot authenticate — the route must be public, protected by the signature rather than a session.
  5. The certificate has quietly expired. Browsers warn people; a server-to-server call just fails.

Nothing is arriving at all

If the log is empty rather than showing failures, the problem is upstream of your endpoint. Check in this order:

  1. Is the endpoint active? A deactivated endpoint receives nothing.
  2. Is it subscribed to the event you expect? An easy thing to get wrong when adding a new one.
  3. Has the signing secret expired? Expired secrets stop deliveries with no log entry at all — the single likeliest cause of a working integration going quiet (link Verifying and rotating your signing secret).
  4. Has your monthly delivery allowance run out? Also silent, also no log entry, and it resets at the start of the next calendar month (link Webhook event reference).
  5. Is Webhooks still on your plan? Losing the feature stops delivery (link Changing your plan).
  6. Send a test payload. If the test appears in the log and real events do not, it is the subscription or the allowance. If the test does not appear either, it is the secret or the feature.

That last step is the fastest diagnostic available — it separates "we are not sending" from "you are not receiving" in one click.

When to disable rather than let it fail

Deactivate an endpoint when:

  • Your server is down for planned maintenance
  • It has been failing for a day or more and nobody is fixing it today
  • You are rebuilding the integration
  • You are about to rotate a secret and cannot accept two (link Verifying signatures)

Leaving a broken endpoint active costs you real things: failed deliveries still consume your monthly allowance, and a log full of failures hides the one that matters. Deactivate, fix, reactivate, then test.

Good to know

The log is per endpoint, so several endpoints subscribed to one event each keep their own history.

A test delivery appears in the log like any other, so you can inspect exactly what was sent and what came back.

Retries do not skip the queue. A retried delivery is dispatched like a new one, so give it a moment.

Your response body is stored — so do not return anything sensitive from a webhook endpoint. A plain 200 with an empty body is ideal.

Where to go next