RevKeen Docs
Fundamentals

Idempotency

Safely retry mutations using the Idempotency-Key header

Send an Idempotency-Key on every mutation so RevKeen can safely replay a stored response when your request times out or your client retries.

Every POST, PATCH, and PUT that moves money or creates a resource must set Idempotency-Key. Network retries without a key can produce duplicate charges, invoices, or refunds.

The header

POST /v2/invoices HTTP/1.1
Host: api.revkeen.com
x-api-key: rk_live_...
Idempotency-Key: 3d4e1b2c-1f5a-4c9b-9e0e-5a1c8a5a2f7a
Content-Type: application/json
RuleDetail
Accepted methodsPOST, PATCH, PUT. GET and DELETE do not honour the header.
Recommended valueUUID v4 (36 chars).
Accepted valueAny string, 1–255 chars, scoped per API key.
Lifetime24 hours from first use.
Expiry behaviourAfter expiry the key is forgotten; reusing it starts a new mutation.

Replay semantics

When RevKeen receives a request with an Idempotency-Key it has seen before in the last 24 hours:

  1. Same key + same body + same path → return the stored response (status, body, headers). The underlying resource is not mutated again.
  2. Same key + different body409 Conflict with error.code = "idempotency_conflict". The original stored response is not returned.
  3. Same key on a different endpoint → treated as a new mutation; keys are scoped per method + path.

The Idempotency-Replayed: true response header is set when you receive a stored response.

Which errors are stored

Only final responses are cached. In-flight state (for example a pending gateway call) is not cached, so an immediate retry during processing may return 409 idempotency_in_progress — back off briefly and retry.

ScenarioStored?Retry behaviour
2xx successYesRetry returns the stored success response.
4xx validation / permission errorYesRetry returns the same 4xx.
5xx after the gateway call completedYesRetry returns the stored 5xx.
5xx before the gateway call completedNoRetry may re-attempt the gateway call.
Network timeout (no response received)NoRetry is safe — the key will either match or replay.

Interaction with SDK auto-retries

All official SDKs automatically attach an Idempotency-Key to safe-to-retry requests and transparently retry 5xx, 429, and network errors up to 3 times.

If you set Idempotency-Key yourself, the SDK uses your value. You only need to override this for cross-process retry scenarios (queue worker replays, durable workflows).

Examples

curl https://staging-api.revkeen.com/v2/refunds \
  -H "x-api-key: $REVKEEN_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "charge": "ch_01HT...", "amount": 1500 }'
import { RevKeen } from "@revkeen/sdk";
import { randomUUID } from "node:crypto";

const client = new RevKeen({ apiKey: process.env.REVKEEN_API_KEY! });

const refund = await client.refunds.create(
  { charge: "ch_01HT...", amount: 1500 },
  { idempotencyKey: randomUUID() },
);
import (
  "github.com/google/uuid"
  revkeen "github.com/RevKeen/sdk-go"
)

refund, err := client.Refunds.Create(ctx, &revkeen.RefundCreateParams{
    Charge: revkeen.String("ch_01HT..."),
    Amount: revkeen.Int64(1500),
}, revkeen.WithIdempotencyKey(uuid.NewString()))
use RevKeen\RevKeenClient;
use Ramsey\Uuid\Uuid;

$client = new RevKeenClient(['api_key' => getenv('REVKEEN_API_KEY')]);

$refund = $client->refunds->create(
    ['charge' => 'ch_01HT...', 'amount' => 1500],
    ['idempotency_key' => Uuid::uuid4()->toString()]
);

Best practices

  • Generate one key per logical operation, not per HTTP retry. A background job that charges a customer should use the same key across all in-process retries; the next scheduled run uses a fresh key.
  • Persist the key alongside the record you are creating (invoice, charge, refund). Reuse it if the job is re-run.
  • Do not reuse keys across different request bodies — that produces a 409 idempotency_conflict.
  • Expect 5xx to be replayed on retry. A stored error is not a reason to abandon; surface it to the operator so they can decide whether to retry with a new key.

See also

On this page