Skip to main content
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 duplicate
Endpoint/api/v1/idempotent

Testing 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 - REST
import { 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