Skip to main content

What is Idempotency?

Idempotency ensures that making the same API request multiple times produces the same result as making it once. This is critical for:
  • Network failures: Retry safely without creating duplicates
  • Timeouts: Resubmit requests that timed out
  • Distributed systems: Handle concurrent requests

How It Works

Include an idempotencyKey in your POST requests:
{
  "idempotencyKey": "order-12345-abc",
  ...
}
If you send the same key within 24 hours:
  1. The system recognizes the duplicate
  2. Returns the original response without re-executing
  3. No duplicate resources are created

Implementation

Generate Unique Keys

Use a consistent strategy for generating keys:
// Option 1: Use your order ID
const idempotencyKey = `order-${orderId}`;

// Option 2: UUID
const idempotencyKey = crypto.randomUUID();

// Option 3: Composite key
const idempotencyKey = `${customerId}-${orderId}-${timestamp}`;

Example: Safe Order Creation

async function createOrder(order) {
  // Key based on your order ID ensures idempotency
  const idempotencyKey = `create-order-${order.id}`;
  
  try {
    const response = await fetch('https://api.crbtrack.com/api/v1/orders/create', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        idempotencyKey,
        ...orderData
      })
    });
    
    const data = await response.json();
    return data;
    
  } catch (error) {
    // Safe to retry - same idempotencyKey won't create duplicate
    if (shouldRetry(error)) {
      return createOrder(order);  // Same key = same result
    }
    throw error;
  }
}

Idempotency Rules

  • Length: 8-64 characters
  • Characters: Alphanumeric, hyphens, underscores
  • Uniqueness: Must be unique per operation type
  • Expiry: Keys expire after 24 hours
  • Keys are scoped to your API key (not global)
  • Same key can be used for different endpoints
  • Example: order-123 for /orders/create is different from order-123 for /orders/void
  • First request: Operation executes, response cached
  • Duplicate request: Cached response returned (HTTP 200)
  • Different payload, same key: Error returned (409 Conflict)

Endpoints Supporting Idempotency

EndpointidempotencyKey
POST /api/v1/orders/createRequired
POST /api/v1/manifests/submitOptional
POST /api/v1/orders/voidRequired
POST /api/v1/labels/{id}/reprintRequired

Common Patterns

Pattern 1: Order ID as Key

Best for e-commerce integrations:
const idempotencyKey = `order-${order.id}`;
✅ Simple and predictable
✅ Natural deduplication if order already processed
✅ Easy to debug

Pattern 2: Request Hash

Best for operations without natural IDs:
const idempotencyKey = crypto
  .createHash('sha256')
  .update(JSON.stringify(requestBody))
  .digest('hex')
  .substring(0, 32);
✅ Automatically detects identical requests
⚠️ Different results for slightly different payloads

Pattern 3: Timestamp + ID

Best for retry-heavy workflows:
const idempotencyKey = `${customerId}-${action}-${Date.now()}`;
⚠️ Generates new key on each attempt
⚠️ Doesn’t prevent accidental duplicates

Handling Conflicts

If you send a different payload with the same key:
{
  "success": false,
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "Request payload differs from original request with this idempotencyKey"
  }
}
Resolution: Generate a new unique key for the modified request.

Best Practices

Do

  • Use predictable keys (order IDs)
  • Store keys with requests for debugging
  • Retry with same key on failures

Don't

  • Generate random keys per retry
  • Reuse keys for different operations
  • Rely on idempotency for business logic