> For the complete documentation index, see [llms.txt](https://docs.partssource.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.partssource.com/api/core-concepts/idempotency.md).

# Idempotency

Prevent duplicate operations with idempotency keys

Idempotency ensures that repeated requests with the same parameters produce the same result, preventing duplicate operations. This is critical for operations like order creation where network issues might cause retries.

***

## Idempotent Endpoints

| Endpoint       | Requires Idempotency Key |
| -------------- | ------------------------ |
| `POST /orders` | **Required**             |

{% hint style="warning" %}
Requests to these endpoints without an `Idempotency-Key` header will return `400 Bad Request`.
{% endhint %}

***

## Using the Idempotency Key

Include the `Idempotency-Key` header with a unique UUID:

```bash
curl -X POST https://api.partssource.com/customer/api/orders \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "requesterId": 12345,
    "userId": 67890,
    "facilityId": 1001,
    "shippingAddressId": 2001,
    "billingAddressId": 3001,
    "shippingMethod": "GROUND",
    "quoteItems": [
      {"priceOptionId": "OPT-001", "productId": "CAT-123", "quantity": 1, "price": 100.00}
    ]
  }'
```

***

## Key Requirements

| Requirement    | Details                                                  |
| -------------- | -------------------------------------------------------- |
| **Format**     | UUID with dashes: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
| **Uniqueness** | Must be unique per logical operation                     |
| **Generation** | Generate client-side using UUID v4                       |
| **Reuse**      | Same key can be reused for retries of the same operation |

***

## How It Works

```
First Request (key: abc-123)
    |
[1] Validate key format (UUID)
    |
[2] Check cache → Not found
    |
[3] Mark as "processing" (5 min timeout)
    |
[4] Execute operation
    |
[5] Cache result (24 hours)
    |
Return response (HTTP 200/201)

Retry Request (same key: abc-123)
    |
[1] Check cache → Found
    |
[2] Return cached response
    |
Response includes: X-Idempotency-Cached: true
```

***

## Request Body Validation

The API validates that retry requests have the same body as the original:

```bash
# Original request
curl -X POST .../orders \
  -H "Idempotency-Key: abc-123" \
  -d '{"userId": 100, "items": [...]}'

# Retry with SAME body → Returns cached result
curl -X POST .../orders \
  -H "Idempotency-Key: abc-123" \
  -d '{"userId": 100, "items": [...]}'

# Retry with DIFFERENT body → Returns 409 Conflict
curl -X POST .../orders \
  -H "Idempotency-Key: abc-123" \
  -d '{"userId": 200, "items": [...]}'  # Different userId!
```

***

## Caching Behavior

| Response Code | Cached? | Duration | Retry Behavior                              |
| ------------- | ------- | -------- | ------------------------------------------- |
| `2xx`         | Yes     | 24 hours | Returns cached success                      |
| `4xx`         | **No**  | -        | Allows retry after fixing validation errors |
| `5xx`         | Yes     | 24 hours | Returns cached error                        |

{% hint style="info" %}
**Why are 4xx responses not cached?**

This allows you to fix validation errors and retry with the same idempotency key. If you send invalid data, correct it and retry—no need to generate a new key.
{% endhint %}

***

## Response Header

When a cached response is returned, the API includes a special header:

```http
HTTP/1.1 200 OK
X-Idempotency-Cached: true
Content-Type: application/json

{"success": true, "data": {"orderNumber": "ORD-12345"}}
```

Check for this header to know if your response came from cache:

```csharp
if (response.Headers.TryGetValues("X-Idempotency-Cached", out var values))
{
    Console.WriteLine("Response was cached from previous request");
}
```

***

## Concurrent Request Handling

If a request is still processing when a retry arrives:

```json
{
  "error": "Request is currently being processed."
}
```

**Status Code:** `409 Conflict`

**Action:** Wait a few seconds and retry. The original request should complete shortly.

***

## Error Responses

| Status | Error              | Cause                          | Solution                                          |
| ------ | ------------------ | ------------------------------ | ------------------------------------------------- |
| `400`  | Header missing     | No `Idempotency-Key` provided  | Add the header                                    |
| `400`  | Invalid format     | Key is not a valid UUID        | Use format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` |
| `409`  | Already processing | Request is in progress         | Wait and retry                                    |
| `409`  | Body mismatch      | Key reused with different body | Use a new key for different operations            |

***

## Code Examples

### C\#

```csharp
var idempotencyKey = Guid.NewGuid().ToString();

var request = new HttpRequestMessage(HttpMethod.Post, "/api/orders");
request.Headers.Add("Idempotency-Key", idempotencyKey);
request.Content = JsonContent.Create(orderRequest);

// Store the key for potential retries
await SaveIdempotencyKey(orderId, idempotencyKey);

var response = await httpClient.SendAsync(request);

if (response.Headers.TryGetValues("X-Idempotency-Cached", out var values))
{
    _logger.LogInformation("Response was cached from previous request");
}
```

### TypeScript

```typescript
const idempotencyKey = crypto.randomUUID();

const response = await fetch('https://api.partssource.com/customer/api/orders', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey,
  },
  body: JSON.stringify(orderRequest),
});

if (response.headers.get('X-Idempotency-Cached') === 'true') {
  console.log('Response was cached from previous request');
}
```

### Python

```python
import uuid
import requests

idempotency_key = str(uuid.uuid4())

response = requests.post(
    'https://api.partssource.com/customer/api/orders',
    headers={
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotency_key,
    },
    json=order_request
)

if response.headers.get('X-Idempotency-Cached') == 'true':
    print('Response was cached from previous request')
```

***

## Best Practices

1. **Generate keys client-side** using UUID v4 (or equivalent)
2. **Store keys** with the operation for retry scenarios
3. **Don't reuse keys** for different operations
4. **Implement retry logic** with exponential backoff
5. **Check `X-Idempotency-Cached`** header to know if response was cached
6. **Log idempotency keys** with your order records for debugging

***

## Retry Pattern Example

```csharp
public async Task<OrderResponse> CreateOrderWithRetry(OrderRequest order, int maxRetries = 3)
{
    var idempotencyKey = Guid.NewGuid().ToString();

    for (int attempt = 1; attempt <= maxRetries; attempt++)
    {
        try
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "/api/orders");
            request.Headers.Add("Idempotency-Key", idempotencyKey);
            request.Content = JsonContent.Create(order);

            var response = await _httpClient.SendAsync(request);

            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadFromJsonAsync<OrderResponse>();
            }

            // Don't retry 4xx errors (except 409 "processing")
            if ((int)response.StatusCode >= 400 && (int)response.StatusCode < 500
                && response.StatusCode != HttpStatusCode.Conflict)
            {
                throw new ApiException(await response.Content.ReadAsStringAsync());
            }
        }
        catch (HttpRequestException) when (attempt < maxRetries)
        {
            // Network error - safe to retry with same idempotency key
            await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
        }
    }

    throw new MaxRetriesExceededException();
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.partssource.com/api/core-concepts/idempotency.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
