Skip to main content

Successful delivery

A delivery is considered successful when your endpoint returns any 2xx HTTP status code within the request timeout. Botdog records the status code and response body and stops retrying. Aim to acknowledge with 200 OK as quickly as possible — your handler should hand the work off to a background job and reply, rather than do the work inline. Long-running handlers risk being treated as failures and retried.

Failure and retry schedule

If your endpoint returns a non-2xx status code, times out, or is unreachable, the delivery is marked as RETRYING and Botdog tries again with exponential backoff. After 6 attempts, the delivery is marked FAILED and Botdog gives up on that event. The webhook itself stays active and continues to receive future events. The backoff is capped at 24 hours per wait, so this schedule won’t grow further even if limits change.

Delivery statuses

Each delivery moves through one of these statuses: You can see counts and recent deliveries — including status code, response body, attempt number, and next-attempt time — on the webhook’s detail page in the dashboard.

At-least-once delivery and idempotency

Because Botdog retries on failure, your endpoint may receive the same event more than once — for example if your handler succeeds but the response is lost in transit. Design handlers to be idempotent:
  • Use the top-level id field on each payload as a deduplication key.
  • Persist id after processing, and ignore deliveries you’ve already seen.

Pausing, resuming, and deleting

From the Webhooks dashboard you can:
  • Pause a webhook to stop new deliveries without losing the configuration. Events that fire while paused are not queued for later.
  • Resume to start receiving events again.
  • Delete a webhook to remove it entirely. In-flight retries for that webhook stop on the next attempt.

Testing your endpoint

Use Send test event on the webhook’s detail page to dispatch a sample payload for any event type you’ve subscribed to. Test events use the same shape and signature scheme as production deliveries, with stub contact data. They count as real deliveries in the history view so you can verify the full flow end to end.
Mappings built from a test event before 2026-09-23 should be re-checked. Until that date the test payload sent the event label as eventName, where real deliveries send humanReadableEventName — so a field mapping built from the test button arrived blank on the first real delivery. Test payloads now send humanReadableEventName (and still send eventName, so nothing that already maps it breaks), and carry every field a real lead event carries. If you built an integration from a test event before that date, compare it against a real delivery in the history view.
Test events are for proving your endpoint is reachable and your signature check is correct. Where you can, build your field mapping against a real delivery: test payloads carry stub contact data, and tools that infer fields from a sample — Zapier and Make among them — will happily lock onto the stub.