idempotency-patterns
Use when designing idempotent APIs, handling retries safely, or preventing duplicate operations. Covers idempotency keys, at-most-once semantics, and duplicate prevention.
What this skill does
# Idempotency Patterns
Patterns for designing APIs and systems that handle retries safely without duplicate side effects.
## When to Use This Skill
- Designing APIs that handle retries safely
- Implementing idempotency keys
- Preventing duplicate operations
- Building reliable payment/order systems
- Handling network failures gracefully
## What is Idempotency?
```text
Idempotent operation: Same result regardless of how many times executed
f(x) = f(f(x)) = f(f(f(x))) = ...
Examples:
- GET /user/123 → Always returns same user (idempotent)
- DELETE /user/123 → User deleted once, subsequent calls no-op (idempotent)
- POST /orders → Creates new order each time (NOT idempotent)
```
## Why Idempotency Matters
```text
Network reality:
Client ──request──> Server
<──response── (lost!)
Client doesn't know if request succeeded.
Should it retry?
Without idempotency:
- Retry creates duplicate order
- Customer charged twice
- Inventory decremented twice
With idempotency:
- Retry returns same result
- No duplicate side effects
- Safe to retry
```
## HTTP Method Idempotency
| Method | Idempotent | Safe | Notes |
| ------ | ---------- | ---- | ----- |
| GET | Yes | Yes | No side effects |
| HEAD | Yes | Yes | No side effects |
| OPTIONS | Yes | Yes | No side effects |
| PUT | Yes | No | Replace entire resource |
| DELETE | Yes | No | Delete is idempotent (already deleted = no-op) |
| POST | No | No | Creates new resource |
| PATCH | Maybe | No | Depends on implementation |
## Idempotency Key Pattern
### Concept
```text
Client generates unique key, server tracks processed keys
Request 1:
POST /payments
Idempotency-Key: abc-123
{amount: 100}
→ Process payment, store result with key abc-123
Request 2 (retry):
POST /payments
Idempotency-Key: abc-123
{amount: 100}
→ Find stored result for abc-123, return same response
→ No duplicate payment
```
### Implementation
```text
Idempotency store schema:
┌──────────────────────────────────────────────────┐
│ idempotency_key │ request_hash │ response │ ttl │
├──────────────────────────────────────────────────┤
│ abc-123 │ sha256(...) │ {...} │ 24h │
└──────────────────────────────────────────────────┘
Flow:
1. Receive request with idempotency key
2. Check if key exists in store
3. If exists:
a. Verify request_hash matches (same request)
b. Return stored response
4. If not exists:
a. Process request
b. Store response with key
c. Return response
```
### Key Generation
```text
Client-generated keys (recommended):
- UUID v4: 550e8400-e29b-41d4-a716-446655440000
- ULID: 01ARZ3NDEKTSV4RRFFQ69G5FAV
- Custom: {client_id}-{timestamp}-{random}
Requirements:
- Globally unique
- Unpredictable (prevent guessing)
- Client controls key
```
### Request Fingerprinting
```text
Verify retry is same request (not just same key):
request_hash = hash(
method,
path,
body,
relevant_headers
)
If idempotency_key exists but request_hash differs:
→ Return 422: "Idempotency key reused with different request"
```
## At-Most-Once vs At-Least-Once
### At-Most-Once
```text
Operation executes 0 or 1 time, never more.
Use when: Duplicate is worse than missing
- Payment processing
- Order creation
- Resource provisioning
Implementation: Idempotency keys with deduplication
```
### At-Least-Once
```text
Operation executes 1 or more times.
Use when: Missing is worse than duplicate
- Event notifications
- Log ingestion
- Analytics events
Implementation: Retry until acknowledged, handle duplicates downstream
```
### Exactly-Once (Hard)
```text
Operation executes exactly 1 time.
Extremely difficult in distributed systems.
Usually achieved through:
- At-least-once delivery + idempotent processing
- Distributed transactions (2PC)
- Saga pattern with compensation
```
## Duplicate Detection Strategies
### Strategy 1: Idempotency Key Store
```text
Store: Redis or database
Key: idempotency_key
Value: {
status: "processing" | "completed" | "failed",
response: {...},
created_at: timestamp,
expires_at: timestamp
}
TTL: 24-72 hours typically
```
### Strategy 2: Natural Key Deduplication
```text
Use business identifiers:
- Order: {customer_id}-{cart_id}-{timestamp}
- Payment: {order_id}-{amount}-{currency}
- Transfer: {sender}-{receiver}-{reference}
Check if natural key exists before processing.
```
### Strategy 3: Database Constraints
```text
CREATE TABLE orders (
id UUID PRIMARY KEY,
idempotency_key VARCHAR(255) UNIQUE,
...
);
INSERT fails if idempotency_key already exists.
```
### Strategy 4: Optimistic Locking
```text
UPDATE accounts
SET balance = balance - 100, version = version + 1
WHERE id = 123 AND version = 5;
If version changed, retry with new version.
Prevents concurrent duplicate updates.
```
## Handling In-Flight Requests
```text
Problem: Request A starts, Request B (retry) arrives before A completes
Solution 1: Lock on idempotency key
- First request acquires lock
- Retry waits or returns "processing"
Solution 2: Status tracking
- Store "processing" status immediately
- Retry sees "processing", waits or returns 409
Response for in-flight:
HTTP 409 Conflict
{
"error": "Request with this idempotency key is still processing",
"retry_after": 5
}
```
## Idempotency in Different Contexts
### Payment APIs
```text
POST /charges
Idempotency-Key: {uuid}
{
"amount": 1000,
"currency": "usd",
"source": "tok_visa"
}
Critical: Never charge twice
Store: idempotency_key → charge_id, status, response
TTL: 24-48 hours
```
### Message Queues
```text
Producer:
- Include message_id in payload
- Retry with same message_id
Consumer:
- Track processed message_ids
- Skip if already processed
Deduplication window: Based on expected retry window
```
### Database Operations
```text
Insert with idempotency:
INSERT INTO orders (id, idempotency_key, ...)
VALUES (gen_id(), 'abc-123', ...)
ON CONFLICT (idempotency_key) DO NOTHING
RETURNING *;
If conflict, fetch existing record.
```
### Event Sourcing
```text
Events naturally idempotent by sequence:
- Event ID: {aggregate_id}-{sequence_number}
- Reject if sequence already exists
- Replay is safe (events are immutable)
```
## Best Practices
### Key Storage
```text
- Use fast store (Redis) for hot path
- Persist to database for durability
- Set appropriate TTL (24-72 hours typical)
- Clean up expired keys
```
### Error Handling
```text
If processing fails:
1. Store failure response with key
2. Client retries get same error
3. Client must use NEW key to try again
This prevents infinite retry loops on bad requests.
```
### Documentation
```text
Document clearly:
- Which endpoints require idempotency keys
- Key format requirements
- TTL for stored results
- Error responses for duplicates
```
### Client Implementation
```text
1. Generate idempotency key before first attempt
2. Store key locally until confirmed success
3. Retry with SAME key on network failure
4. Generate NEW key for genuinely new requests
5. Don't reuse keys across different operations
```
## Common Pitfalls
```text
1. Storing only success responses
→ Store failures too, otherwise retry creates duplicate
2. Short TTL
→ Client might retry after TTL expires, causing duplicate
3. Not hashing request body
→ Different requests with same key processed differently
4. Race conditions on concurrent retries
→ Use locks or atomic operations
5. Not handling partial failures
→ Use sagas or compensation for multi-step operations
```
## Related Skills
- `api-design-fundamentals` - API design patterns
- `rate-limiting-patterns` - Handling retries
- `distributed-transactions` - Multi-step operations
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.