Delivery & Retries

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.

AttemptFires 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, the timestamp field and the signature's t= value are regenerated on every attempt, so v2 retry bodies are not byte-identical. In v1 the original event document is re-sent unchanged (no timestamp/t=), so v1 retry bodies are identical. Either way, the event id (event_id in v2, _id/id in 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 200 within 5 seconds, before doing real work
  • Process asynchronously after acknowledging
  • Deduplicate on event_id (v2) / _id (v1) — never on timestamp
  • Tolerate out-of-order and duplicate deliveries
  • Verify the signature before processing (see Signature Verification)