DoloresDocs
Webhooks

Retry behavior

Backoff schedule, terminal conditions, manual replay.

When your endpoint returns non-2xx or times out, Dolores retries with exponential backoff until either you 2xx or the attempt count exhausts.

Backoff schedule

AttemptWait before this attempt
1 (initial)0s
25s
330s
45m
530m
62h
75h
810h
924h
10+terminal failure

Total time-window: about a day from the initial trigger. After that the delivery is marked failed and we stop retrying automatically.

What counts as a retryable failure

ResponseRetried?
2xxNo — success
408 (request timeout)Yes
429 (too many requests)Yes
4xx (other)No — terminal
5xxYes
Connection refused / DNS / TLS errorYes
Connection timeout (>15s)Yes

4xx responses other than 408/429 are treated as permanent rejections — we assume your endpoint has authoritatively decided not to accept this delivery. If you didn't mean that, fix the bug and use manual replay.

Manual replay

Three paths to retry a failed delivery:

Admin UI

Settings → Webhooks → Deliveries → Replay on any row.

REST API

curl -X POST \
  "https://app.meetdolores.ai/v1/events/evt_.../deliveries/whd_.../retry" \
  -H "Authorization: Bearer $DOLORES_API_KEY"

This drops a fresh delivery row into the queue. The worker picks it up within a second.

Programmatic

For a bulk recovery (e.g., your endpoint was down for hours and you want to replay everything that failed in that window):

# List events that have at least one failed delivery
curl "https://app.meetdolores.ai/v1/events?limit=100" \
  -H "Authorization: Bearer $DOLORES_API_KEY"
# For each, list deliveries and retry the failed ones

We may add a ?status=failed query param to filter directly — until then, list-and-filter on the client.

Timeout

Dolores aborts the HTTP request after 15 seconds. Your handler should respond within that window — do the actual work asynchronously (drop the event on a queue, return immediately).

If you need to do real work synchronously and it might be slow, set up a slim "ack" endpoint that 200s fast and a separate worker that processes the queue.

Delivery order

We deliver events in the order they're produced per endpoint, on a best-effort basis. Two appointment.* events for the same appointment land in causal order — but retries can shuffle this. If event A is retrying when event B arrives, B can land first.

Your handler should be state-machine-aware, not order-aware:

  • previous_attributes.status tells you what the appointment was; data.object.status tells you what it is now.
  • Persist the appointment's current state, not the event sequence.

If previous_attributes.status doesn't match what you have on file, you've either missed an earlier event (retrying soon) or you're seeing a replay (drop the duplicate). Either way, prefer the latest event's data.object as ground truth and reconcile via GET /v1/appointments/{id} if confused.