Skip to main content
A retried write must not execute twice. ENSC gives you two mechanisms, one at the HTTP layer and one at the conversion layer. @ensc/sdk applies the first by default; the second is a property of the conversion reference.

The idempotency key

Send X-ENSC-Idempotency-Key on every write (POST, PUT, PATCH, DELETE); the API also accepts the alias Idempotency-Key. The value is 8 to 64 characters of A-Z, a-z, 0-9, _ and -; anything else is refused with 400 ENSC_VALIDATION_FAILED. The API honours the key on every write route: conversions and their events, vouchers and payouts, account resolution, transfers, webhook endpoints, test events and credential writes.
  • Repeating a request with the same key and the same body returns the original response. The replay carries the header X-ENSC-Idempotent-Replay: true, so you can tell a replay from a fresh execution.
  • The same key with a different body is refused with 409 ENSC_IDEMPOTENCY_CONFLICT.
  • Keys are scoped to your account and environment (the same key sent with a Sandbox key and then a Live key does not replay the Sandbox answer), and remembered for 24 hours.
The idempotency key is part of the signed request string (see Encrypting and signing requests), so it cannot be altered in transit. A retry keeps the same idempotency key but must re-sign with a fresh timestamp and nonce: a nonce is accepted once (ENSC_NONCE_REUSED), while the idempotency key is what makes the second attempt a replay.

What the SDK does

The SDK generates one idempotency key per logical call and keeps it constant across its automatic retries. It retries only network errors, timeouts and the statuses 500, 502, 503 and 504 (maxRetries, default 2); it never retries a 4xx, including 429. Because the key is stable across those retries, a transparently retried POST cannot double-execute. If your own code retries a call after a timeout, that is a new logical call to the SDK with a new key. For a conversion, rely on the reference instead.

Conversion references

POST /v1/conversions accepts your own reference (op:<type>:<hex>, 8 to 64 hex characters or dashes after the type, so a UUID fits); otherwise ENSC mints one.
  • Re-posting a reference you already used returns the existing conversion with 200, so a retried create can never double-issue.
  • If the first attempt was cut off between the insert and the voucher (a timeout, a signer or bank-rail error), the conversion is left at created with lastError set. Re-posting the same reference finishes it instead of returning the stuck row; ensc.conversions.voucher(reference) does the same.
  • A reference another account already used is refused (409 ENSC_REFERENCE_CONFLICT), and one that does not match the pattern for its type is 400 ENSC_VALIDATION_FAILED.
Reporting the same conversion event twice (events.submitted, events.confirmed, events.failed) is harmless.