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

# Idempotency

> Use Idempotency-Key headers on Spree Store API mutations to safely retry carts, checkouts, and payments without creating duplicate side effects.

The Store API supports idempotency keys on mutating endpoints. This lets you safely retry requests without risking duplicate side effects — for example, adding an item to cart twice or creating duplicate payments due to network timeouts.

## How It Works

Include an `Idempotency-Key` header with a unique value (e.g., a UUID) on any supported request. The API will:

1. **First request** — process normally and cache the response for 24 hours
2. **Duplicate request** (same key, same parameters) — return the cached response without re-executing the operation
3. **Key reuse with different parameters** — return a `422` error to prevent misuse

```bash theme={"theme":"night-owl"}
curl -X POST https://api.mystore.com/api/v3/store/carts/cart_xxx/items \
  -H "X-Spree-API-Key: pk_xxx" \
  -H "X-Spree-Token: abc123" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"variant_id": "variant_xxx", "quantity": 1}'
```

If the client retries this exact request with the same idempotency key, the API returns the original response with an `Idempotent-Replayed: true` header — without adding the item again.

## Supported Endpoints

| Endpoint                                         | Actions                  |
| ------------------------------------------------ | ------------------------ |
| `POST /carts`                                    | Cart creation            |
| `PATCH /carts/:id`                               | Cart updates             |
| `POST /carts/:id/complete`                       | Order completion         |
| `POST /carts/:id/items`                          | Adding items to cart     |
| `POST /carts/:id/payment_sessions`               | Payment session creation |
| `PATCH /carts/:id/payment_sessions/:id/complete` | Payment completion       |
| `POST /carts/:id/coupon_codes`                   | Applying coupon codes    |
| `POST /carts/:id/store_credits`                  | Applying store credits   |

Idempotency keys are ignored on `GET`, `DELETE`, and other non-supported actions.

## Key Requirements

* Must be a string of **255 characters or less**
* Should be unique per distinct operation (UUIDs are recommended)
* Keys are scoped per API key — different API keys can use the same idempotency key without conflict
* Cached responses expire after **24 hours**

## Response Headers

When a cached response is replayed, the API sets:

```
Idempotent-Replayed: true
```

You can use this header to detect replayed responses in your application or logging.

## Error Handling

### Key Reuse with Different Parameters

If you send a request with an idempotency key that was previously used with different request parameters (different body, different path), the API returns a `422` error:

```json theme={"theme":"night-owl"}
{
  "error": {
    "code": "idempotency_key_reused",
    "message": "This Idempotency-Key has already been used with different request parameters."
  }
}
```

### Key Too Long

Keys longer than 255 characters return a `400` error:

```json theme={"theme":"night-owl"}
{
  "error": {
    "code": "invalid_request",
    "message": "Idempotency-Key must be 255 characters or less."
  }
}
```

### Server Errors

`5xx` responses are **not cached**. If the server fails to process your request, you can safely retry with the same idempotency key and the request will be re-executed.

## SDK Usage

The Spree SDK supports idempotency keys via the `idempotencyKey` option:

```typescript theme={"theme":"night-owl"}
import { createClient } from '@spree/sdk'

const client = createClient({
  baseUrl: 'http://localhost:3000',
  publishableKey: 'pk_xxx',
})

// Add item to cart with idempotency key
await client.carts.items.create(cartId, {
  variant_id: 'variant_xxx',
  quantity: 1,
}, {
  idempotencyKey: '550e8400-e29b-41d4-a716-446655440000',
})
```

## Best Practices

* **Generate a new key for each distinct operation.** Use a UUID (v4) or another random identifier. Never reuse keys across different operations.
* **Store the key before sending the request.** If the request fails due to a network error, retry with the same key to ensure the operation is only performed once.
* **Don't use idempotency keys for GET requests.** GET requests are naturally idempotent and the API ignores the header on read-only endpoints.
* **Let keys expire naturally.** Cached responses expire after 24 hours. Don't rely on idempotency keys for longer-term deduplication.

## Relationship with Optimistic Locking

The Store API also uses [optimistic locking](/api-reference/store-api/introduction) via the `state_lock_version` field on orders. These mechanisms complement each other:

| Mechanism                                     | Prevents                          | Scope                              |
| --------------------------------------------- | --------------------------------- | ---------------------------------- |
| **Idempotency keys**                          | Duplicate operations from retries | Per-request deduplication          |
| **Optimistic locking** (`state_lock_version`) | Concurrent modifications          | Conflict detection between clients |

Use both together for robust checkout flows: idempotency keys protect against network retries, while `state_lock_version` detects when another client has modified the order.
