---
title: Idempotency
description: Make publisher retries and duplicate destination deliveries safe with Stormedo idempotency keys and stable request IDs.
---

Stormedo has two separate idempotency boundaries:

1. Publisher idempotency prevents the same submission from creating multiple request resources.
2. Receiver idempotency prevents duplicate delivery attempts from repeating a business side effect.

You usually need both.

## Publisher idempotency

Send an `Idempotency-Key` header when creating a request:

```http
Idempotency-Key: order-123-created
```

The reservation is scoped to the project and retained for 24 hours.

During that window:

- The same key with the same input returns the original request.
- The same key with different input returns `409 Conflict`.
- A request without a key always creates new work.

Use a stable business identifier when your application already knows what one logical operation means.

```bash
curl --request POST \
  'https://api.stormedo.com/send?url=https://your-app.example/webhook' \
  --header "Authorization: Bearer $STORMEDO_TOKEN" \
  --header 'Idempotency-Key: order-123-created' \
  --header 'Content-Type: application/json' \
  --data '{"order_id":"ord_123"}'
```

## SDK submission behavior

The JavaScript and Python SDKs generate one key per logical send. If the API call is interrupted before receiving a response, the SDK safely retries once with the same key.

Pass `idempotencyKey` in JavaScript or `idempotency_key` in Python when you want the key to represent a business operation across separate application calls.

## Receiver idempotency

Stormedo provides at-least-once delivery. A destination can process an attempt successfully before Stormedo records the response, so the same request can arrive again.

Every attempt includes:

```http
Stormedo-Request-Id: req_33uZSQsVaf8aZjDuzkuAq
```

After authenticating the delivery, store this stable request ID before, or atomically with, the business side effect.

A common database pattern is:

```text
BEGIN
  insert request_id into processed_deliveries with a unique constraint
  if the insert succeeded, apply the business side effect
COMMIT
```

When the unique insert reports that the ID already exists, return a successful response without applying the side effect again.

## Do not use attempt identity

Attempts are operational records. They can change between deliveries of the same logical request.

Deduplicate with the stable `Stormedo-Request-Id`, not a timestamp, response status, or attempt number.

## Batch and schedule keys

Batch creation requires a key for the complete batch and can also include an idempotency key for each item.

Schedule creation requires an `Idempotency-Key`. Repeating the same definition with the same key returns the original schedule, while changing the definition returns `409 Conflict`.
