Request Idempotency

Request idempotency allows you to safely retry API requests without worrying about duplicate operations. When you include an Idempotency-Key header with your request, our API will ensure that multiple requests with the same key produce the same result.
Idempotency is particularly important for critical operations like sending emails, creating resources, or processing payments where duplicate actions could cause problems.

How It Works

When you make a request with an Idempotency-Key header:
  1. First Request: The API processes your request normally and stores the response
  2. Subsequent Requests: If you retry with the same key, the API returns the stored response instead of processing the request again
  3. Automatic Cleanup: Idempotency keys expire after 24 hours

Key Selection Best Practices

A client generates an idempotency key, which is a unique key that the server uses to recognize subsequent retries of the same request. How you create unique keys is up to you, but we suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions. Idempotency keys are up to 255 characters long.

Key Generation Examples

// Using crypto.randomUUID() (Node.js 14.17+)
const idempotencyKey = crypto.randomUUID();
// Result: "550e8400-e29b-41d4-a716-446655440000"

// Using a library like uuid
import { v4 as uuidv4 } from 'uuid';
const idempotencyKey = uuidv4();

// Custom format with timestamp
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2);
const idempotencyKey = `msg_${timestamp}_${random}`;
// Result: "msg_1705317045123_k2j5h8n3m1"
Key Collision Risk: With V4 UUIDs, the probability of generating duplicate keys is approximately 1 in 5.3 x 10^36. For practical purposes, this is negligible even at massive scale.

Usage Example

Include the Idempotency-Key header with any POST request:
curl -X POST https://api.ahasend.com/v2/accounts/acct_123/messages \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: msg_20240115_001" \
  -d '{
    "from": "[email protected]",
    "to": "[email protected]",
    "subject": "Welcome!",
    "html": "<h1>Welcome to our service!</h1>"
  }'

Response Behavior

The API responds differently based on the idempotency key status:
Status: 200 OK (or appropriate success status)Headers:
  • Standard response headers
  • No special idempotency headers
Body: Normal response content
{
  "id": "msg_abc123",
  "status": "queued",
  "created_at": "2024-01-15T10:30:00Z"
}

Best Practices

Unique Keys

Use unique, descriptive keys that won’t conflict with other operations. Consider including timestamps or UUIDs.
Good: user_123_welcome_20240115_001
Bad: request_1

Retry Logic

Implement exponential backoff when retrying requests. Always use the same idempotency key for retries.
const maxRetries = 3;
let attempt = 0;

while (attempt < maxRetries) {
  try {
    return await makeRequest(idempotencyKey);
  } catch (error) {
    if (error.status === 409) {
      // Request in progress, wait and retry
      await sleep(Math.pow(2, attempt) * 1000);
    } else {
      throw error;
    }
  }
  attempt++;
}

Key Expiration

Keys expire after 24 hours. Don’t reuse keys after this period as the behavior is undefined.

Error Handling

Handle different response codes appropriately:
  • 409: Wait and retry (concurrent request)
  • 412: Don’t retry (original failed)
  • 2xx with Idempotent-Replayed: true header: Success (replayed)

Error Scenarios

Implementation Details

The following technical details are provided for transparency but are not required for basic usage.

Request Hashing

The API uses SHA256 hashing of the request body to detect changes between requests with the same idempotency key. This ensures that different requests don’t accidentally match.

Concurrency Protection

PostgreSQL advisory locks prevent race conditions when multiple requests with the same idempotency key arrive simultaneously. The first request proceeds normally while subsequent requests wait.

Storage Duration

Idempotency records are automatically cleaned up after 24 hours. This prevents the idempotency table from growing indefinitely while providing a reasonable retry window.
For high-volume applications, consider implementing client-side deduplication in addition to server-side idempotency to reduce unnecessary API calls.