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
| Attempt | Wait before this attempt |
|---|---|
| 1 (initial) | 0s |
| 2 | 5s |
| 3 | 30s |
| 4 | 5m |
| 5 | 30m |
| 6 | 2h |
| 7 | 5h |
| 8 | 10h |
| 9 | 24h |
| 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
| Response | Retried? |
|---|---|
| 2xx | No — success |
| 408 (request timeout) | Yes |
| 429 (too many requests) | Yes |
| 4xx (other) | No — terminal |
| 5xx | Yes |
| Connection refused / DNS / TLS error | Yes |
| 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 onesWe 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.statustells you what the appointment was;data.object.statustells 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.