---
title: Replay and retention
description: Replay retained events on demand and track delivery status through the event lifecycle.
url: https://pr-1-3b5c652a9824.thally.app/replay
---

# Replay and retention

Replay retained events on demand and track delivery status through the event lifecycle.

OrbitRail retains every event for 30 days after ingestion. During this window you can replay events on demand and inspect their delivery status at any time.

## Replaying events

Send a `POST` to `/v1/events/{eventId}/replay` to queue a new delivery attempt for a retained event:

```bash
curl -X POST \
  https://events.orbitrail.example/v1/events/evt_abc123/replay \
  -H "X-OrbitRail-Key: $ORBITRAIL_API_KEY"
```

| Response | Meaning |
|---|---|
| `202` | Replay accepted for delivery |
| `404` | Event not found or no longer retained (older than 30 days) |
| `429` | Another replay was requested within the last 30 seconds |

### Replay cooldown

Repeated replay requests for the same event are rate-limited to one every 30 seconds (`deliveryPolicy.manualReplayCooldownSeconds`). This prevents accidental flooding of downstream destinations.

### SDK helpers

Use the exported helper functions to check eligibility before making the API call:

```typescript
import { canReplayEvent, canRequestReplay } from "@orbitrail/events";

// Check if the event is still within the 30-day retention window
canReplayEvent(15);    // true  — 15 days old
canReplayEvent(30);    // true  — exactly 30 days
canReplayEvent(30.01); // false — past the retention window
canReplayEvent(-1);    // false — negative age is invalid

// Check replay eligibility including cooldown
canRequestReplay(15, 31); // true  — retained and cooldown elapsed
canRequestReplay(15, 29); // false — cooldown has not elapsed
canRequestReplay(31, 60); // false — event is past the retention window
```

## Delivery status

Query the current delivery state with `GET /v1/events/{eventId}`:

```bash
curl https://events.orbitrail.example/v1/events/evt_abc123 \
  -H "X-OrbitRail-Key: $ORBITRAIL_API_KEY"
```

The response includes the event's current lifecycle state and attempt count:

```json
{
  "eventId": "evt_abc123",
  "status": "delivered",
  "attempts": 1,
  "lastAttemptAt": "2026-01-15T10:30:00Z"
}
```

### Lifecycle states

Events progress through these states:

| Status | Description |
|---|---|
| `accepted` | Event received and queued for first delivery attempt |
| `delivering` | A delivery attempt is currently in progress |
| `delivered` | Event successfully delivered to the destination |
| `retrying` | Last attempt failed; another attempt is scheduled within the 72-hour retry window |
| `failed` | All 12 attempts exhausted or the 72-hour retry window expired |

### Validating status values

Use `isDeliveryStatus` to check whether a string is one of the five recognized lifecycle states:

```typescript
import { isDeliveryStatus } from "@orbitrail/events";

isDeliveryStatus("delivered"); // true
isDeliveryStatus("retrying");  // true
isDeliveryStatus("unknown");   // false
```

### Retry eligibility

Use `canRetryDelivery` to check whether a failed event is still within the automatic retry window:

```typescript
import { canRetryDelivery } from "@orbitrail/events";

canRetryDelivery(48);  // true  — within the 72-hour window
canRetryDelivery(72);  // true  — at the boundary
canRetryDelivery(73);  // false — past the retry window
canRetryDelivery(-1);  // false — negative age is invalid
```