← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

API Idempotency Keys

api_design PHP 7.0+ Intermediate

Also Known As

idempotency key Idempotency-Key header duplicate request prevention

TL;DR

A client-generated unique key sent with non-idempotent requests — the server stores the response and returns it unchanged if the same key is received again, preventing duplicate operations.

Explanation

POST requests are not idempotent — retrying a failed payment POST could charge twice. Idempotency keys solve this: client generates a UUID per operation, sends it as Idempotency-Key header, server stores (key → response) and on retry returns the cached response without re-executing. Keys typically expire after 24 hours. Implementation: check key in Redis before processing, store result atomically with key, return stored result on duplicate. Stripe, PayPal, and all major payment APIs implement this pattern.

Diagram

sequenceDiagram
    participant C as Client
    participant S as Server
    participant R as Redis
    C->>S: POST /payments<br/>Idempotency-Key: uuid-123
    S->>R: GET idempotency:uuid-123
    R-->>S: null - not seen before
    S->>S: Process payment
    S->>R: SET idempotency:uuid-123 = response
    S-->>C: 200 OK - charged
    Note over C,S: Network timeout - client retries
    C->>S: POST /payments<br/>Idempotency-Key: uuid-123
    S->>R: GET idempotency:uuid-123
    R-->>S: cached response
    S-->>C: 200 OK - same response, not charged again

Watch Out

If two requests with the same idempotency key carry different payloads, you must return an error — silently accepting the second request with different data breaks the contract and causes subtle bugs.

Common Misconception

Retrying with the same payload is safe because the server checks for duplicates — without idempotency keys the server has no way to know if a request is a retry or a new operation with identical data.

Why It Matters

A network timeout on a payment POST — did it succeed or not? Without idempotency keys, retrying risks charging twice; with them, the retry is safe and returns the original result.

Common Mistakes

  • Using request body hash as idempotency key — two different users making the same purchase would share a key.
  • Not expiring idempotency keys — storing responses forever wastes storage.
  • Not making the key storage and operation atomic — a race condition between checking and storing creates duplicates.
  • Idempotency keys on GET requests — GET is already idempotent; keys are only needed for state-changing operations.

Avoid When

  • Do not use idempotency keys as a substitute for proper transaction handling — the key prevents duplicate requests, not concurrent ones.
  • Avoid short key expiry windows for payment flows where network timeouts can cause retries hours later.

When To Use

  • Use idempotency keys for any non-idempotent POST operation that could be safely retried: payments, order creation, message sending.
  • Implement server-side key storage for the duration clients may reasonably retry (typically 24 hours to 7 days).
  • Return the cached response immediately when a duplicate key is received — do not re-execute the operation.

Code Examples

💡 Note
The bad example processes every POST independently; a network timeout causes the client to retry and the user is charged twice — the fix deduplicates on the server using the client-supplied key.
✗ Vulnerable
// No idempotency — double-charge on retry:
POST /api/payments
{ "amount": 1000, "card": "tok_visa" }
// Network timeout after 5s — did it succeed?
// Retry:
POST /api/payments
{ "amount": 1000, "card": "tok_visa" }
// Result: charged twice
✓ Fixed
// Idempotency key — safe retry:
POST /api/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{ "amount": 1000, "card": "tok_visa" }
// Network timeout...
// Retry with SAME key:
POST /api/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
// Server: key exists, return cached 200 response
// Result: charged once, client has the receipt

// PHP server implementation:
$cached = $redis->get('idempotency:' . $key);
if ($cached) return json_decode($cached); // Return stored response
$result = $paymentGateway->charge($amount, $card);
$redis->setex('idempotency:' . $key, 86400, json_encode($result));
return $result;

Added 16 Mar 2026
Edited 5 Apr 2026
Views 28
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Amazonbot 8 Perplexity 6 Ahrefs 2 Unknown AI 2 SEMrush 2 Google 1 ChatGPT 1
crawler 22
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Accept an Idempotency-Key header on POST endpoints — store the key and response; return the cached response for duplicate requests with the same key within a 24-hour window
📦 Applies To
PHP 7.0+ any web api
🔗 Prerequisites
🔍 Detection Hints
POST endpoint for payment or order creation without idempotency key support; client retries causing duplicate charges
Auto-detectable: ✗ No semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Medium ✗ Manual fix Fix: Medium Context: Function Tests: Update

✓ schema.org compliant