---
title: "API idempotency"
description: "Learn how to safely resend API requests without performing the same operation multiple times."
url: "https://docs.adyen.com/development-resources/api-idempotency"
source_url: "https://docs.adyen.com/development-resources/api-idempotency.md"
canonical: "https://docs.adyen.com/development-resources/api-idempotency"
last_modified: "2020-07-30T16:39:00+02:00"
language: "en"
---

# API idempotency

Learn how to safely resend API requests without performing the same operation multiple times.

[View source](/development-resources/api-idempotency.md)

The Adyen API supports [idempotency](https://tools.ietf.org/html/rfc7231#section-4.2.2), allowing you to retry a request multiple times while only performing the action once. This helps avoid unwanted duplication in case of failures and retries. For example, in the case of a timeout error, it is possible to safely retry sending the same API payment call multiple times with the guarantee that the payment detail will only be charged once.

The accounting rules in the Adyen payments platform take care of most potential double-processing issues that can impact payment [modifications](/get-started-with-adyen/adyen-glossary/#payment-modifications-definition). For example, the following default rules apply:

* [Captures](/online-payments/capture): when partial captures are allowed, it is not possible to capture a higher amount than the authorized one.
* [Refunds](/online-payments/refund): when multiple refunds are allowed, by default the total refunded value cannot exceed the captured amount.

To minimize unwanted side effects when requests are duplicated, you can also take the following actions on your end:

* Implement asynchronous server-to-server [webhooks](/development-resources/webhooks). For example, this approach helps keep track of missing responses, a common consequence of a data transmission timeout.
* Enable idempotency in your API requests. The Adyen API supports idempotency on POST requests (other request types such as GET, DELETE and PUT are idempotent by definition).

## Enable idempotency

To submit a request for idempotent processing, send a request with the `idempotency-key:<key>` in the header.

The `<key>` is a unique identifier for the message with a maximum of 64 characters. We recommend using a UUID. If you do not receive a response (for example, in case of a timeout), you can safely retry the request with the same HTTP header. If the Adyen payments platform already processed the request, the response to the first attempt will be returned without duplication.

To verify that a request was processed idempotently, check the `idempotency-key` HTTP header returned in the response.

Make sure that you code for case-insensitive HTTP headers.

Here is an example of how to include the `idempotency-key` in a [/payments](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments) request:

#### curl

```bash
curl https://checkout-test.adyen.com/v72/payments \
-H 'x-api-key: {hint:Your API key from your Customer Area.}ADYEN_API_KEY{/hint}' \
-H 'idempotency-key: YOUR_IDEMPOTENCY_KEY' \
-H 'content-type: application/json' \
-d '{
  "merchantAccount": "{hint:Name of your merchant account.}YOUR_MERCHANT_ACCOUNT{/hint}",
  "reference": "My first Adyen test payment",
  "amount": {
    "value": {hint:10 euros in minor units}1000{/hint},
    "currency": "EUR"
  },
	"paymentMethod": {
    "type": "scheme",
    "encryptedCardNumber": "test_4111111111111111",
    "encryptedExpiryMonth": "test_03",
    "encryptedExpiryYear": "test_2030",
    "encryptedSecurityCode": "test_737"
  }
}'
```

#### Java

```java
// Import the required classes
import com.adyen.Client;
import com.adyen.enums.Environment;
import com.adyen.service.checkout.PaymentsApi;
import com.adyen.model.checkout.*;

// Setup the client and service.
Client client = new Client("ADYEN_API_KEY", Environment.TEST);
PaymentsApi paymentsApi = new PaymentsApi(client);

// Create a payment request.
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.setMerchantAccount("YOUR_MERCHANT_ACCOUNT");
CardDetails cardDetails = new CardDetails();
cardDetails.encryptedCardNumber("test_4111111111111111")
    .encryptedSecurityCode("test_737")
    .encryptedExpiryMonth("test_03")
    .encryptedExpiryYear("test_2030");
paymentRequest.setPaymentMethod(new CheckoutPaymentMethod(cardDetails));
Amount amount = new Amount().currency("EUR").value(1000L);
paymentRequest.setAmount(amount);
paymentRequest.setReference("My first Adyen test payment with an API library/SDK");
paymentRequest.setReturnUrl("https://your-company.example.com/checkout?shopperOrder=12xy..");

// Add your idempotency key.
RequestOptions requestOptions = new RequestOptions();
requestOptions.setIdempotencyKey("YOUR_IDEMPOTENCY_KEY");

// Make a request to the /payments endpoint.
PaymentResponse paymentResponse = paymentsApi.payments(paymentRequest, requestOptions);
```

#### PHP

```php
// Set up the client and service.

$client = new \Adyen\Client();

$client->setXApiKey("ADYEN_API_KEY");
$client->setEnvironment(\Adyen\Environment::TEST);
$client->setTimeout(30);

$service = new \Adyen\Service\Checkout\PaymentsApi($client);

$requestOptions['idempotencyKey'] = "YOUR_IDEMPOTENCY_KEY";

// Create a PaymentMethod object.
$paymentMethod = new CheckoutPaymentMethod();
$paymentMethod
    ->setType("scheme")
    ->setEncryptedCardNumber("test_4111111111111111")
    ->setEncryptedExpiryMonth("test_03")
    ->setEncryptedExpiryYear("test_2030")
    ->setEncryptedSecurityCode("test_737");

// Create an Amount object.
$amount = new Amount();
$amount
    ->setValue(1500)
    ->setCurrency("EUR");

// Create the PaymentRequest object.
$paymentRequest = new PaymentRequest();
$paymentRequest
    ->setMerchantAccount("YOUR_MERCHANT_ACCOUNT")
    ->setPaymentMethod($paymentMethod)
    ->setAmount($amount)
    ->setReference("My first Adyen test payment with an API library/SDK")
    ->setReturnUrl("https://your-company.example.com/...");

// Make a /payments request.
$result = $service->payments($paymentRequest, $requestOptions);
```

#### C\#

```cs
using Adyen;
using Adyen.Model.Checkout;
using Adyen.Service;
using Environment = Adyen.Model.Environment;

// Create a paymentRequest object.
var amount = new Amount("USD", 1000);
var paymentRequest = new PaymentRequest
{
    Reference = "My first Adyen test payment with an API library/SDK",
    Amount = amount,
    ReturnUrl = @"https://your-company.example.com/...",
    MerchantAccount = "YOUR_MERCHANT_ACCOUNT",
};

// Set up the client and service.
var config = new Config
{
    XApiKey = "ADYEN_API_KEY",
    Environment = Environment.Test
};
var client = new Client(config);
var checkout = new PaymentsService(client);

// Add your idempotency key.
var requestOptions = new RequestOptions { IdempotencyKey= "YOUR_IDEMPOTENCY_KEY" };

// Make a /payments request.
var paymentResponse = checkout.Payments(paymentRequest, requestOptions);
```

#### NodeJS (JavaScript)

```js
// Step 1: Require the parts of the module you want to use.
const { Client, CheckoutAPI} = require('@adyen/api-library');

// Step 2: Initialize the client object.
const client = new Client({apiKey: "ADYEN_API_KEY", environment: "TEST"});

// Step 3: Initialize the API object.
const checkoutApi = new CheckoutAPI(client);

// Step 4: Create the request object.
const paymentRequest = {
    amount: {
      currency: "USD",
      value: 1000 // value in minor units
    },
    reference: "My first Adyen test payment with an API library/SDK",
    paymentMethod: {
      type: "scheme",
      encryptedCardNumber: "test_4111111111111111",
      encryptedExpiryMonth: "test_03",
      encryptedExpiryYear: "test_2030",
      encryptedSecurityCode: "test_737"
    },
    shopperReference: "YOUR_SHOPPER_REFERENCE",
    storePaymentMethod: true,
    shopperInteraction: "Ecommerce",
    recurringProcessingModel: "CardOnFile",
    returnUrl: "https://your-company.example.com/...",
    merchantAccount: "YOUR_MERCHANT_ACCOUNT"
};

// Optional: Add your idempotency key.
const requestOptions = { "idempotencyKey": "YOUR_IDEMPOTENCY_KEY" };

// Step 5: Make a /payments request.
checkoutApi.PaymentsApi.payments(paymentRequest, requestOptions)
  .then(paymentResponse => console.log(paymentResponse.pspReference))
  .catch(error => console.log(error));
```

#### Ruby

```ruby
require 'adyen-ruby-api-library'

adyen = Adyen::Client.new
adyen.api_key = "ADYEN_API_KEY"

headers = {"idempotency-key": "YOUR_IDEMPOTENCY_KEY"}

response = adyen.checkout.payments_api.payments({
  :amount => {
    :currency => "EUR",
    :value => 1000
  },
  :reference => "My first Adyen test payment with an API library/SDK",
  :paymentMethod => {
    :type => "scheme",
    :encryptedCardNumber => "test_4111111111111111",
    :encryptedExpiryMonth => "test_03",
    :encryptedExpiryYear => "test_2030",
    :encryptedSecurityCode => "test_737"
  },
  :returnUrl => "https://your-company.example.com/checkout/",
  :merchantAccount => "YOUR_MERCHANT_ACCOUNT"
})
```

## Key scope and validity time

Keys are stored at a company account level. The system checks that the idempotency keys are unique to the company account. Idempotency keys are valid for a minimum period of 7 days after first submission.

If you are targeting multiple regional endpoints simultaneously, idempotency keys will not be checked for duplication in other regions.

## Security considerations

Generate unique idempotency keys per request using the **version 4 (random)** UUID type to prevent two API credentials under the same account from accessing each others responses.

Lowering the access level for an API credential does not prevent them from retrieving past responses if the user still has access to previously used keys.

## Error handling

### Transient errors

In rare instances, you could receive a transient error. This is a temporary error that has no side effects and can be retried. For example, it could come from a race condition of sending two payment requests with the same idempotency key at the same time. One will end up being processed while the other will return a transient error.

The response will contain an HTTP header **transient-error** with the value **true**, which indicates that the request can be retried at a later time using the same idempotency key. If the API does not return a transient error header, or returns a header with a value of false, do not retry the request. If you submit a duplicate request before the first request has completed, the API returns an **HTTP 422 – Unprocessable Entity** or **HTTP 409 - Conflict** status with the error code **704: "request already processed or in progress"**.

### Retries and exponential backoff

Use an exponential backoff strategy when retrying transactions to avoid flooding the API and running into rate-limiting. See [the Wikipedia article on exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) for more information.

### Designing for resilience

To enable idempotent behavior, Adyen must retain a consistent, stateful data store to compare each request against. Idempotent processing relies on this data store being available. In the unlikely event that this data store should become unavailable, the API returns an **HTTP 503 – Service Unavailable** status with the error code **703: "required resource temporarily unavailable"**. The system marks the request as a transient error (see above).

If there are many of these responses, you can either:

* Pause processing and retry the requests later (if this is feasible).
* Fall back to non-idempotent processing by not submitting the `idempotency-key` HTTP header.
