How TelegraMD delivers webhooks, retries failures, and what that means for your consumer's idempotency handling. Applies to both v1 and v2 payloads.
Acknowledging a delivery
Your endpoint must respond with an HTTP 2xx status within 5 seconds. Anything else is treated as a failed delivery and retried:
- Non-2xx response (3xx/4xx/5xx) → failure.
- No response within 5 seconds → the request is cancelled and recorded as a timeout (failure), even if your server ultimately processed it.
Acknowledge first, then do the work. Return 200 immediately and process the event asynchronously. If your handler does slow work inline and exceeds 5 seconds, TelegraMD will treat the delivery as failed and redeliver the same event — so a slow-but-successful endpoint produces duplicates.
Retry schedule
Failed deliveries are retried automatically with exponential backoff. A sweeper runs every 5 minutes and picks up deliveries whose backoff has elapsed.
| Attempt | Fires after the previous failure by |
|---|---|
| Initial delivery | — |
| Retry 1 | ~5 min |
| Retry 2 | ~10 min |
| Retry 3 | ~20 min |
| Retry 4 | ~40 min |
| Retry 5 | ~80 min |
That is 1 initial attempt + 5 retries = 6 total, over roughly 2 hours 35 minutes. Delays are quantized to the 5-minute sweep, so treat them as "at least this long." After the 6th failed attempt the delivery is marked permanently failed and is not retried again.
Idempotency
Delivery is at-least-once — you may receive the same event more than once (most commonly from the 5-second timeout described above). Deduplicate on the event id:
- v2:
event_id - v1:
_id/id
The event id is stable across every retry of the same event — every attempt delivers the same underlying event, so the id never changes. Use it as your idempotency key. Note that stability applies to the event, not the bytes on the wire: v2 rebuilds the envelope on each attempt (fresh timestamp and signature), while v1 re-sends the original document unchanged.
Don't dedupe on the timestamp or the raw body — only on the event id. In v2, thetimestampfield and the signature'st=value are regenerated on every attempt, so v2 retry bodies are not byte-identical. In v1 the original event document is re-sent unchanged (notimestamp/t=), so v1 retry bodies are identical. Either way, the event id (event_idin v2,_id/idin v1) is the only stable key.
Ordering
There is no ordering guarantee. Retries of an older event can arrive after newer events for the same entity. Don't assume a monotonic per-entity sequence; reconcile against the current state (e.g. order_status) rather than assuming arrival order.
The v2 timestamp field cannot help here either: it is the delivery-attempt time, regenerated on every retry, so a retried older event carries a newer timestamp than a fresh event. v1's createdAt is the stable event-creation time; v2 has no equivalent field today.
Checklist
- Respond
200within 5 seconds, before doing real work - Process asynchronously after acknowledging
- Deduplicate on
event_id(v2) /_id(v1) — never ontimestamp - Tolerate out-of-order and duplicate deliveries
- Verify the signature before processing (see Signature Verification)
