@ensc/sdk applies the first by default; the second is a property of the conversion reference.
The idempotency key
SendX-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.
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
createdwithlastErrorset. 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 is400 ENSC_VALIDATION_FAILED.
events.submitted, events.confirmed, events.failed) is harmless.