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

# Idempotency

> Make writes safe to retry on the ScripX API: send an Idempotency-Key on order placement so a network blip or a 429 retry returns the original order instead of double-placing a trade.

A network timeout tells you nothing about what happened on the server: the order may have been placed, or not. Retrying blind risks a duplicate trade. An **idempotency key** removes the risk: re-sending the same key returns the original result instead of creating a second one, so you can retry fearlessly.

## Where it applies

Send an `Idempotency-Key` header on every [`POST /v1/orders`](/api-reference/trading/orders-create). This is the money-moving write where a duplicate matters most.

```bash theme={"dark"}
curl -X POST "https://api.scripxhq.com/v1/orders" \
  -H "X-ScripX-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f3d2c9a-order-1" \
  -d '{"side": "sell", "scheme": "RODTEP", "amount_paise": 100000000, "min_pct_bps": 9600}'
```

Re-send the exact same request with the same key and you get the **original order** back, not a new one. The key is echoed on the response `Idempotency-Key` header so you can confirm it was honoured.

<Note>
  The official [SDKs](/sdks) attach an `Idempotency-Key` automatically on order placement (a fresh UUID per call), and let you pass your own when you want to control it. If you build the request yourself, set the header yourself.
</Note>

## How to choose a key

* **One key per logical order.** Derive it from something stable in your own system, a UUID you persist with the order row, or a natural key like `sell:{scrip_no}:{attempt-group}`. The same intent must always produce the same key.
* **Never reuse a key for a different order.** The key identifies *this* order; a new order needs a new key.
* **Keep it opaque and bounded.** A UUID is ideal. Do not encode secrets or PII into it.

## What it protects against

| Situation                                  | Without a key      | With the same key              |
| ------------------------------------------ | ------------------ | ------------------------------ |
| Network timeout, you retry                 | Risk of two orders | The original order is returned |
| `429`, you retry after `Retry-After`       | Risk of two orders | The original order is returned |
| Your process crashes mid-send and restarts | Risk of two orders | The original order is returned |

## Pattern: retry loop

<CodeGroup>
  ```python Python theme={"dark"}
  from scripx import ScripxClient, ScripxError

  sx = ScripxClient("scripx_live_…")
  key = ScripxClient.new_idempotency_key()   # persist this with your order row before sending

  def place():
      return sx.orders.create(
          side="sell", scheme="RODTEP", amount_paise=100_000_000,
          min_pct_bps=9600, idempotency_key=key,   # same key on every retry
      )

  for attempt in range(5):
      try:
          order = place()
          break
      except ScripxError as e:
          if e.status == 429:
              # honour Retry-After, then retry with the SAME key; no duplicate can result
              continue
          raise
  ```

  ```typescript TypeScript theme={"dark"}
  import { Scripx, ScripxError } from "@scripx/sdk";

  const sx = new Scripx({ apiKey: "scripx_live_…" });
  const key = Scripx.newIdempotencyKey();   // persist with your order row before sending

  async function place() {
    return sx.orders.create({
      side: "sell", scheme: "RODTEP", amount_paise: 100_000_000,
      min_pct_bps: 9600, idempotencyKey: key,   // same key on every retry
    });
  }

  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      const order = await place();
      break;
    } catch (e) {
      if (e instanceof ScripxError && e.status === 429) continue; // retry, same key
      throw e;
    }
  }
  ```
</CodeGroup>

The SDKs already retry `429` and transient `5xx` for you with backoff, reusing the key you pass, so most integrations never write this loop. It is shown here to make the guarantee explicit: even across process restarts, the same key can never place a second order.

The rule is simple: **generate the key once, before the first attempt, and reuse it on every retry of the same order.** Generating a new key per attempt defeats the protection.

## Related

* [Place an order](/api-reference/trading/orders-create), the endpoint that honours the key.
* [Rate limits](/rate-limits), why retries happen and how to back off.
* [Errors](/errors), what to retry and what not to.
