---
title: Publishing events
description: How to publish events to OrbitRail, including idempotency, payload requirements, and error handling.
url: https://pr-1-3b5c652a9824.thally.app/publishing
---

# Publishing events

How to publish events to OrbitRail, including idempotency, payload requirements, and error handling.

Publish events by sending a `POST` request to `/v1/events`. OrbitRail accepts the event and delivers it to every registered destination automatically.

## Request requirements

Every publish request must include:

- **`X-OrbitRail-Key` header** — your OrbitRail API key
- **`Idempotency-Key` header** — a unique key for deduplication (required)
- **`Content-Type: application/json`** — the only accepted content type
- **JSON body** with `type` (string) and `payload` (object) fields
- **Body size** at most 256 KiB (262,144 bytes)

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

const response = await fetch("https://events.orbitrail.example/v1/events", {
  method: "POST",
  headers: {
    [deliveryPolicy.authenticationHeader]: apiKey,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": deliveryPolicy.requiredContentType,
  },
  body: JSON.stringify({
    type: "user.signup",
    payload: { userId: "usr_42", plan: "pro" },
  }),
});
```

## Idempotency

The `Idempotency-Key` header is required on every publish request. If you reuse the same key within the 24-hour idempotency window (`deliveryPolicy.idempotencyWindowHours`), OrbitRail returns the original accepted event instead of creating a duplicate.

Use this to safely retry publish calls without risking double-delivery:

```typescript
const idempotencyKey = `signup-${userId}-${Date.now()}`;

// Safe to retry — same key within 24 hours returns the original response
await publishEvent(idempotencyKey, eventPayload);
```

## Delivery behavior

Once accepted, OrbitRail delivers the event with these guarantees:

- **Timeout**: each delivery attempt times out after 8 seconds
- **Retries**: failed attempts retry up to 12 times over a 72-hour window
- **Backoff**: retries use exponential backoff with jitter to avoid thundering-herd effects

## Error responses

| Status | Condition |
|---|---|
| `202` | Event accepted for delivery |
| `413` | Payload exceeds the 256 KiB limit |
| `415` | Content type is not `application/json` |

## Validating payload size

Check the payload size before sending to avoid `413` errors:

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

const body = JSON.stringify({ type: "order.completed", payload: orderData });

if (new TextEncoder().encode(body).length > deliveryPolicy.maximumPayloadBytes) {
  throw new Error("Event payload exceeds the 256 KiB limit");
}
```