Skip to content

Idempotency

Idempotency guarantees that performing the same operation multiple times produces the same result as performing it once. In the context of payment and transfer APIs, this property is critical: if a network failure occurs after you send a transfer request but before you receive the response, you can safely retry the same request without risking a duplicate transfer.

Idempotency-Key Header

To make a request idempotent, include the Idempotency-Key header with a unique identifier:

HeaderRequiredDescription
Idempotency-KeyRecommendedA unique key (UUID v4) that identifies this logical operation. The server uses this key to detect and deduplicate retries.

The key must be a string of up to 128 characters. We recommend UUID v4 for guaranteed uniqueness.

Which Endpoints Support It

Idempotency behavior varies by HTTP method:

MethodIdempotent by DefaultIdempotency-Key SupportedNotes
GETYesNoRead operations are naturally idempotent. Repeating a GET returns the same data.
DELETEYesNoDeleting an already-deleted resource returns a consistent result.
POSTNoYesCreate operations are not idempotent by default. Use Idempotency-Key to make them safe to retry.
PUTYesNoFull-resource updates are naturally idempotent.
PATCHDependsOptionalPartial updates may or may not be idempotent depending on the operation.

POST endpoints

The Idempotency-Key header is primarily designed for POST endpoints that create resources, such as /api/v1/transfer/command/create. These are the operations most vulnerable to duplication from retries.

How It Works

When you include an Idempotency-Key header in a request, the server follows this process:

1. Receive request with Idempotency-Key
2. Check if this key has been seen before
   ├── NEW KEY:
   │   ├── Process the request normally
   │   ├── Store the key, request body hash, and response
   │   └── Return the response
   └── EXISTING KEY:
       ├── Compare the request body hash with the stored one
       ├── If body matches → return the stored response (no reprocessing)
       └── If body differs → return 409 Conflict (key collision)
3. Key expires after 24 hours

The server stores the association between the idempotency key and the response for 24 hours. During this window, any retry with the same key and the same request body will return the original response without re-executing the operation.

Key Generation

Use UUID v4 to generate idempotency keys. Each key must be unique per logical operation.

Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

Key generation by language:

Example: Preventing Duplicate Transfers

Consider a scenario where you need to create a transfer and want to ensure it is not duplicated even if you must retry.

Step 1: Generate an idempotency key before the request.

Step 2: Send the transfer creation request with the key.

Step 3: If the request fails due to a network error, retry with the same key and body.

A successful response on either the initial request or a replay:

Transfer created (or replayed)200
{
  "version": "1.3.0",
  "timestamp": 1709337600000,
  "success": true,
  "code": "2000",
  "message": "SUCCESS",
  "data": {
    "transferId": "txn_789xyz",
    "status": "COMPLETED",
    "amount": "100.00",
    "currency": "USD"
  }
}

Error Handling

Key Collision (Different Body)

If you reuse an idempotency key with a different request body, the server returns 409 Conflict with error code T1023:

Key collision — different body409
{
  "version": "1.3.0",
  "timestamp": 1709337600000,
  "success": false,
  "code": "T1023",
  "message": "DUPLICATE_REQUEST",
  "data": null
}

This is a safety mechanism. Each idempotency key is bound to a specific request body hash. You cannot reuse a key to perform a different operation.

Expired Key

Idempotency keys expire after 24 hours. After expiration, the same key can be used again — but the server will treat it as a new request, not a replay. Avoid relying on key reuse after expiration; always generate a new key for new operations.

Network Errors

If you receive a network timeout or connection error (no HTTP response at all), it is safe to retry with the same idempotency key. The server either:

  • Never received the request — it will process it as new.
  • Received and processed it — it will return the cached response.

In both cases, no duplicate transfer is created.

Best Practices

Generate the key before the retry loop

Create the idempotency key before entering your retry logic. If you generate a new key on each retry, you lose the deduplication guarantee and may create duplicate resources.

One key per logical operation

Each distinct business operation should have its own idempotency key. Do not reuse a key from a previous transfer to create a new, different transfer. Different operations require different keys.

Key expiration window

Keys are valid for 24 hours. Design your retry logic to complete within this window. For most use cases, a retry strategy with exponential backoff (1s, 2s, 4s) and a maximum of 3-5 attempts will resolve well within this limit.

Store the key for reconciliation

When creating transfers, log the idempotency key alongside the request in your system. If you need to investigate whether a transfer was created, you can look up the key to determine whether the server processed the request.

:::caution Do not use Idempotency-Key as Nonce The Idempotency-Key and X-Nonce headers serve different purposes. The nonce is part of the HMAC signature and must be unique per HTTP request (including retries). The idempotency key must be the same across retries of the same logical operation. Never set them to the same value. :::

Next Steps

Lampay API Documentation