Skip to content

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.

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 new accepted submission without a key creates new work.
  • After 24 hours, the key no longer protects a repeated submission. Preserve business idempotency at the receiver as well.

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"}'

A timeout, lost connection, or API 500, 502, or 503 can occur after durable acceptance. Treat the result as unknown, not proof that Stormedo rejected the work.

  1. Keep the original project, endpoint, idempotency key, and input.
  2. Retry with that same key and unchanged input within 24 hours. Do not generate a new key for recovery.
  3. Store the returned request ID and inspect its delivery status. A brief initial 404 can mean the read projection has not caught up.
  4. If the original submission had no key, inspect request history and your receiver before resubmitting. A new submission can duplicate accepted work.

A key conflict (409) requires investigation of the original input. Increasing retry frequency or changing the key does not resolve that conflict safely. Use bounded backoff for publisher retries. Destination retry settings apply only after acceptance.

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.

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 verifying the delivery signature and exact body, commit the stable request ID and the business side effect in the same database transaction. Committing the ID first can lose work if the process crashes before applying the effect.

A common database pattern is:

Plain 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.

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. Replay creates a new request ID. Use an additional stable business-operation ID in the payload when the same event must be processed only once across replays and separate submissions.

These transactions protect effects in the same database. For an external API call, use that API’s idempotency mechanism or a transactional outbox; a local deduplication row cannot make a remote side effect atomic. See Receive deliveries for runnable receiver examples.

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.