Uses: Python · TypeScript · CLI · REST API
cURL samples assume
BASE=https://api.engini.io/v1 and AUTH="x-api-key: $ENGINI_API_KEY" - the setup from the REST walkthrough.How an event ends up undelivered
An event’sdelivery is the state of its latest attempt:
To tell the two kinds of
pending apart, read the event (step 3): an empty attempts list is backlog, and a last attempt with next_attempt_at set is a scheduled retry.
Five dead-lettered events in a row auto-disable the destination (max_consecutive_failures, default 5).
1. Check the destination
Start here - if the destination has auto-disabled, nothing is being delivered at all, andlast_failure usually names the cause.
2. Find the events that did not arrive
The event log comes back newest first. There is no server-side filter on delivery state, so filter on your side.3. Read why each attempt failed
GET /v1/triggers/events/{eventId} is the only place with the real attempt history: each attempt’s status code, a trimmed error, how long it took, and when the next one is due.
4. Fix the receiver, then prove the fix
Deploy your fix, then send a test event through the real delivery path before replaying anything. It is signed with the destination’s secret and tells you synchronously what your receiver answered. The test endpoint answers409 while the destination is disabled, so re-enable it first (step 5) if you need to.
5. Re-enable the destination
Settingstatus back to active also resets the failure counter to zero.
Re-enabling resumes most of the backlog on its own. The backstop sweep picks up events from the last 24 hours - and destinations paused mid-retry - within about a minute, with no replay needed. New events are delivered normally from here on. Wait that minute, then re-list (step 2) before deciding what still needs a replay in step 6.
6. Replay what was missed
A replay queues one more attempt, due straight away, and answers202. It does not deliver inline, so check the event again a few seconds later. The attempt numbering continues from where it left off, so a replay gets whatever retries the event had left - none, if it had already used all six.
Only two kinds of event still need a replay after re-enabling: dead_lettered events, and backlog events (pending with no attempts) older than 24 hours. Anything newer already went out on its own - replaying it too is harmless (your receiver dedupes on X-Engini-Event-Id), but there is no reason to.
- Skip events with a retry already scheduled. Replaying one as well sends it twice - your receiver’s dedupe on
X-Engini-Event-Idabsorbs that, but there is no reason to. - A replay answers
409(DESTINATION_DISABLED) if the destination is still off. - Your receiver sees the same
X-Engini-Event-Idit may have seen before, so deduplication protects you if an earlier attempt did get through.
Rotate a secret without losing events
Rotation has no overlap window: the old secret stops verifying the moment the new one is minted. Any delivery that lands in between is refused by your receiver - and because that refusal is a4xx, the event is dead-lettered rather than retried. So plan to replay.
1
Rotate
client.triggers.destinations.rotate_secret("prod") / rotateSecret("prod"). The new esec_… is shown once.2
Deploy the new secret to your receiver
As fast as your deploy allows - every event that arrives before this finishes will be dead-lettered.
3
Replay the gap
List the trigger’s events, select those that are
dead_lettered with an occurred_at after the rotation, and replay them (step 6).