Retries & delivery
Three attempts, exponential backoff, eight-second timeout. Build idempotent handlers and use the event ID for deduplication.
Schedule
Three attempts per event, with backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | +1 second |
| 3 | +4 seconds |
If all three fail, the delivery is marked failed in the Hub and not retried automatically. You can manually retry any failed delivery from the Events tab.
Timeout
Each attempt has an 8-second request timeout. If your handler takes longer, return 2xx quickly and process asynchronously.
What's retryable
Retried:
- Any
5xxresponse. 408 Request Timeout.429 Too Many Requests.- Network errors and timeouts.
Not retried:
2xx(success).3xx(redirect — return2xxinstead).4xxother than408/429.
Idempotency
Every event has a stable id field (e.g. evt_a1b2c3d4e5f6). The same event ID persists across retries. Use it to deduplicate:
async function handle(event) {
const seen = await redis.set(`pg:evt:${event.id}`, "1", "NX", "EX", 86400 * 7);
if (seen !== "OK") return; // already processed
await processCompletion(event);
}
A 7-day TTL is more than enough — Playgent retries within seconds, not days.
Ordering
Events are not strictly ordered. Two completions from the same user may arrive out of order, especially around retries. Don't rely on receive order — use completed_at from the payload for time-based logic.
Backpressure
If your handler is slow, Playgent's 8-second timeout will trigger retries. To avoid that:
- Receive the request.
- Verify the signature.
- Enqueue the payload to your own queue (SQS, Cloud Tasks, Inngest, etc.).
- Return
200.
Your worker processes the queue at its own pace.