Skip to main content

Overview

Idempotency ensures that duplicate requests have the same effect as a single request, preventing unintended side effects from network issues, retries, or user errors.

How Idempotency Works

Supported Endpoints

The following endpoints support idempotency:
  • POST /organizations - Organization creation
  • POST /external-payment-instruments - Payment instrument creation
  • POST /quotes - Quote creation
  • POST /orders - Order creation

Implementation

Include an Idempotency-Key header with a unique identifier:
curl -X POST "https://api-sandbox.stablesea.com/v1/organizations" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "name": "Example Organization",
    "contact": {
      "email": "[email protected]",
      "first_name": "John",
      "last_name": "Doe"
    }
  }'

Key Requirements

  • Format: Use UUID v4 format for maximum uniqueness
  • Uniqueness: Each unique operation requires a different key
  • Retention: Keys are stored for 24 hours after first use
  • Scope: Keys are scoped to the API endpoint and organization

Best Practices

Key Generation

import { v4 as uuidv4 } from "uuid";

// Generate a new key for each unique operation
const idempotencyKey = uuidv4();

// For retries, reuse the same key
const response = await fetch("/api/orders", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + apiKey,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify(orderData),
});

Retry Strategy Integration

async function createOrderWithIdempotency(orderData, maxRetries = 3) {
  const idempotencyKey = uuidv4(); // Single key for all retries

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch("/api/orders", {
        method: "POST",
        headers: {
          Authorization: "Bearer " + apiKey,
          "Idempotency-Key": idempotencyKey,
        },
        body: JSON.stringify(orderData),
      });

      if (response.ok) {
        return await response.json();
      }

      // Don't retry 4xx errors (except 429)
      if (
        response.status >= 400 &&
        response.status < 500 &&
        response.status !== 429
      ) {
        throw new Error(`Client error: ${response.status}`);
      }
    } catch (error) {
      if (attempt === maxRetries) throw error;
      await sleep(1000 * Math.pow(2, attempt - 1)); // Exponential backoff
    }
  }
}

Response Behavior

First Request (HTTP 201)

id
string
Unique identifier for the created resource
status
string
Current status of the created resource
idempotency_key
string
The idempotency key used for this request

Duplicate Request (HTTP 200)

Same response returned with HTTP 200 (not 201), indicating the resource was not created again but the original response is returned.

Error Scenarios

Invalid Key Format (HTTP 400)

error.code
string
Error code: INVALID_IDEMPOTENCY_KEY
error.message
string
Human-readable error message explaining the key format requirements
error.details.provided_key
string
The invalid key that was provided in the request

Key Conflict (HTTP 409)

When the same key is used with different request bodies:
error.code
string
Error code: IDEMPOTENCY_KEY_CONFLICT
error.message
string
Explains that the key was already used with different parameters
error.details.original_request_hash
string
Hash of the original request body for comparison
error.details.current_request_hash
string
Hash of the current request body showing the difference

Enterprise Considerations

Compliance & Auditing

  • All idempotency key usage is logged for audit purposes
  • Request hashes are stored (not full request bodies) for privacy
  • Logs include timestamps, API keys (masked), and response status

High-Volume Integration

  • Consider implementing client-side deduplication before API calls
  • Use persistent storage for idempotency keys across application restarts
  • Monitor idempotency key conflicts as they may indicate integration issues

Key Management

  • Development: Use predictable keys for testing (e.g., test-{scenario})
  • Production: Always use cryptographically secure UUIDs
  • Monitoring: Track idempotency key usage patterns for optimization