> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prosperavest.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Send X-ENSC-Idempotency-Key on every write so a retry can never double-execute, and use conversion references so a retried create can never double-issue.

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](/security/encrypting-and-signing#request-signature-ensc-v1)), 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`.

```ts theme={null}
import { randomUUID } from 'node:crypto';

const reference = `op:crypto-issue:${randomUUID()}`;   // store it before you call

const c = await ensc.conversions.create({
  reference,
  type: 'crypto-issue',
  chain: 'celo',
  wallet,
  pair: 'USDC',
  amount: '100',
});
// On a retry with the same reference: the existing conversion, HTTP 200,
// or, if the first attempt stopped at `created`, the finished one.
```

Reporting the same conversion event twice (`events.submitted`, `events.confirmed`, `events.failed`) is harmless.
