Delivery history and retries
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
| Column | What it tells you |
|---|---|
| Event | Which event this was |
| Status | Pending, delivered or failed |
| Response code | What your server answered — 200,
404, 500 |
| Response body | What your server actually said, or the connection error if it never answered |
| Attempts | How many times it has been tried |
| Next retry | When the next attempt is due, if any remain |
| Delivered at | When it succeeded |
| Payload | Exactly 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.updatedreplays 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 see | Usually means | Fix |
|---|---|---|
404 | Wrong URL, or the route was removed or renamed | Check the URL, including its path and any trailing slash |
401 / 403 | Your own app is refusing it — often signature verification failing, or authentication in front of the endpoint | See Verifying signatures; webhook endpoints must not sit behind a login |
405 | The route does not accept POST | Accept POST |
500 | Your code threw. The response body normally shows what | Read the response body in the log |
502 / 503 | Your server or a proxy in front of it was unavailable | Usually resolves; the retries may cover it |
| A timeout or connection error | Nothing answered within 30 seconds — server down, firewall, or your handler is too slow | See below |
| A certificate error | Expired, self-signed or incomplete HTTPS certificate | Fix the certificate chain; self-signed will not be accepted |
The usual suspects, in order
- Your handler does the work before responding. The commonest real cause. Acknowledge with
a
200immediately 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. - Signature verification against a re-encoded body. Every delivery fails while your code looks right (link Verifying signatures).
- A firewall or security product blocking the request. Ask whoever runs your hosting; some web application firewalls block unfamiliar POST traffic by default.
- The endpoint is behind a login. Tabledoo cannot authenticate — the route must be public, protected by the signature rather than a session.
- 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:
- Is the endpoint active? A deactivated endpoint receives nothing.
- Is it subscribed to the event you expect? An easy thing to get wrong when adding a new one.
- 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).
- 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).
- Is Webhooks still on your plan? Losing the feature stops delivery (link Changing your plan).
- 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.