Idempotency
How to use idempotency keys to prevent duplicate operations.
What is Idempotency?
Idempotency ensures that an operation can be executed multiple times with the same result. In APIs, this is crucial to avoid creating duplicate resources when there are network issues or application errors.
How it Works
Beel implements idempotency via the X-Idempotency-Key header. When you include this header in write requests (POST, PUT), the API:
- Stores the idempotency key along with the operation result
- If it receives the same request with the same key, returns the stored result
- Does not execute the operation again
Recommended Usage
POST Requests (creation)
Always use X-Idempotency-Key when creating resources:
curl -X POST "https://app.beel.es/api/v1/facturas" \
-H "X-API-Key: beel_sk_xxx" \
-H "X-Idempotency-Key: factura-orden-12345" \
-H "Content-Type: application/json" \
-d '{
"clienteId": "cliente-123",
"lineas": [
{
"descripcion": "Consulting",
"cantidad": 1,
"precio": 100.00
}
]
}'PUT Requests (update)
Also recommended for critical updates:
curl -X PUT "https://app.beel.es/api/v1/clientes/cliente-123" \
-H "X-API-Key: beel_sk_xxx" \
-H "X-Idempotency-Key: update-client-address-456" \
-H "Content-Type: application/json" \
-d '{
"direccion": {
"calle": "New address 123"
}
}'Key Generation
The idempotency key must be unique for each logical operation you want to perform.
✅ Best Practices
// Use UUID v4
import { v4 as uuidv4 } from 'uuid';
const idempotencyKey = uuidv4(); // "550e8400-e29b-41d4-a716-446655440000"
// Combine identifiers from your system
const idempotencyKey = `invoice-order-${orderId}-${timestamp}`;
// Use external references
const idempotencyKey = `sync-shopify-order-${shopifyOrderId}`;❌ Bad Practices
// DON'T use values that repeat
const idempotencyKey = 'create-invoice'; // ❌ Too generic
const idempotencyKey = Date.now().toString(); // ❌ Can collide
const idempotencyKey = Math.random().toString(); // ❌ Non-deterministicUsage Scenarios
Problem: Network Error
Without idempotency:
1. Client sends request → Timeout
2. Client retries → Creates second invoice ❌
3. Result: Duplicate invoicesWith idempotency:
1. Client sends request with key "abc123" → Timeout
2. Client retries with same key "abc123" → API detects duplicate
3. Result: Returns original invoice ✅Problem: Automatic Retry
// Without idempotency - DANGER
async function createInvoice(data) {
try {
return await api.post('/facturas', data);
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
return await api.post('/facturas', data); // ❌ Creates duplicate
}
}
}
// With idempotency - SAFE
async function createInvoice(data, idempotencyKey) {
const headers = { 'X-Idempotency-Key': idempotencyKey };
try {
return await api.post('/facturas', data, { headers });
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
return await api.post('/facturas', data, { headers }); // ✅ Same key
}
}
}Lifetime
Idempotency keys are stored for 24 hours. After this time, the same key can be reused for a new operation.
Responses
First Request (successful)
HTTP/1.1 201 Created
X-Idempotency-Replay: false
{
"data": {
"id": "factura-abc123",
"numero": "FACT-2025-001",
...
}
}Duplicate Request (with same key)
HTTP/1.1 200 OK
X-Idempotency-Replay: true
{
"data": {
"id": "factura-abc123",
"numero": "FACT-2025-001",
...
}
}The X-Idempotency-Replay: true header indicates the response comes from a previous request.
Limitations
- Keys expire after 24 hours
- Only apply to
POSTandPUTrequests - Do not apply to naturally idempotent operations (
GET,DELETE) - Maximum length: 255 characters
Next Steps
- HTTP Methods — Learn about GET, POST, PUT and DELETE
- Create an invoice — Practical example with idempotency