Delivery & retries
Orchestr delivers each event to every subscribed endpoint and retries failed deliveries on a fixed backoff schedule.
What counts as success
A delivery succeeds when your endpoint returns an HTTP 2xx status within the timeouts below.
| Limit | Value |
|---|---|
| Connect timeout | 3 seconds |
| Read timeout | 30 seconds |
Anything else is a failure and will be retried, including:
- any non-
2xxstatus (a4xxis retried too — your endpoint may be temporarily misconfigured and fixed before attempts run out); - connection refused, DNS failure, or TLS errors;
- a response that exceeds the read timeout.
Retry schedule
A delivery is attempted up to 6 times (1 initial attempt + 5 retries). After each failed attempt, Orchestr waits before the next one:
| After failed attempt | Wait before next attempt |
|---|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 24 hours |
| 6 | — (no further retries) |
The total retry window spans roughly 27 hours from the first attempt. The first attempt is made immediately when the event is produced; retries are dispatched by a poller shortly after each backoff interval elapses.
Delivery states
Each delivery moves through these states:
| State | Meaning |
|---|---|
PENDING | Created, awaiting the first attempt. |
PROCESSING | Currently being delivered. |
SUCCEEDED | Your endpoint returned 2xx. Terminal. |
FAILED | An attempt failed; a retry is scheduled. |
EXHAUSTED | All attempts failed. Terminal — no further delivery. |
INVALID | The event could not be delivered (e.g. the endpoint URL failed validation at send time). Terminal. |
When a delivery reaches EXHAUSTED, Orchestr stops trying. Make sure your endpoint is reliable, and
reconcile against the API (for example by searching transactions or orders) if you
suspect missed events.
Idempotency
The same event can be delivered more than once — for example when your endpoint returns 2xx
but the connection drops before Orchestr records the success, or when several endpoints are
subscribed. Design handlers to be idempotent:
- De-duplicate on the envelope
id. Record processed event IDs and ignore repeats. - Make the side effects of processing safe to apply at-least-once (upserts, conditional updates).
- Do not assume events arrive in order. Use
event_time/createdand the resourcestatusto decide whether an event is newer than what you have already applied.
on receive(event):
if seen(event.id): # already processed → ack and stop
return 200
verify_signature(event) # reject if it does not verify
persist(event.id) # mark as seen
enqueue_for_processing(event) # do slow work asynchronously
return 200