REST API
Idempotency Keys
RequestValidateMedium
What you will learn
- Make a retried POST safe by reusing a stable Idempotency-Key and asserting one resource, not two
- Distinguish 201 Created (first) from 200 replayed (retry) and key-reuse-with-different-body (422)
- Understand why production payment/order endpoints require idempotency to avoid double effects
Description
Make a POST safe to retry: the same Idempotency-Key returns the same resource instead of creating a duplicate (no double-charge).
Test Scenario
// Fire twice with the same key โ assert exactly one resource was created.
const key = 'order-42-attempt';
const first = await request.post('/api/v1/idempotent', {
headers: { 'Idempotency-Key': key },
data: { amount: 100 },
});
expect(first.status()).toBe(201);
const firstId = (await first.json()).data.resourceId;
const retry = await request.post('/api/v1/idempotent', {
headers: { 'Idempotency-Key': key },
data: { amount: 100 },
});
expect(retry.status()).toBe(200);
expect((await retry.json()).data.resourceId).toBe(firstId); // same resource, not a duplicateEndpoint
/api/v1/idempotentTesting Tips
- โขSend a stable Idempotency-Key header; reuse the SAME key on a retry to get the original result back
- โขFirst call โ 201; identical retry โ 200 with the same resourceId and replayed: true
- โขReusing a key with a DIFFERENT body โ 422; omitting the key โ 400
// Playwright API Testing - RESTimport { test, expect } from '@playwright/test';test('REST API request', async ({ request }) => {const response = await request.get('/api/v1/resource');expect(response.ok()).toBeTruthy();expect(response.status()).toBe(200);const data = await response.json();expect(data).toBeDefined();});
Challenge ID: rest-idempotency-keys
2 frameworks available
API Playground
Send requests and inspect responses
Endpoint: /api/v1/idempotent
Press Enter to send