{"title":"API idempotency","category":"default","creationDate":1779533780,"content":"<p>The Adyen API supports\u00a0<a href=\"https:\/\/tools.ietf.org\/html\/rfc7231#section-4.2.2\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">idempotency<\/a>, allowing you to retry a request multiple times while only performing the action once.\u00a0This helps avoid unwanted duplication in case of failures and retries. For example, in the case of a timeout error,\u00a0it 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.<\/p>\n<p>The accounting rules in the Adyen payments platform\u00a0take care of most potential double-processing issues that can impact\u00a0payment\u00a0<a href=\"\/get-started-with-adyen\/adyen-glossary\/#payment-modifications-definition\">modifications<\/a>.\u00a0For example, the following default rules apply:<\/p>\n<ul>\n<li><a href=\"\/online-payments\/capture\">Captures<\/a>: when partial captures are allowed, it is not possible to capture a higher amount than the authorized one.<\/li>\n<li><a href=\"\/online-payments\/refund\">Refunds<\/a>: when multiple refunds are allowed, by default the total refunded value cannot exceed the captured amount.<\/li>\n<\/ul>\n<p>To minimize unwanted side effects when requests are duplicated, you can also take the following actions on your end:<\/p>\n<ul>\n<li>Implement asynchronous server-to-server <a href=\"\/development-resources\/webhooks\">webhooks<\/a>. For example, this approach helps keep track of missing responses, a common consequence of a data transmission timeout.<\/li>\n<li>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).<\/li>\n<\/ul>\n<h2 id=\"enable-idempotency\">Enable idempotency<\/h2>\n<p>To submit a request for idempotent processing, send a request with the <code>idempotency-key:&lt;key&gt;<\/code> in the header.<\/p>\n<p>The\u00a0<code>&lt;key&gt;<\/code>\u00a0is 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.<\/p>\n<p>To verify that a request was processed idempotently, check the <code>idempotency-key<\/code> HTTP header returned in the response.<\/p>\n<div class=\"notices yellow\">\n<p>Make sure that you code for case-insensitive HTTP headers.<\/p>\n<\/div>\n<p>Here is an example of how to include the <code>idempotency-key<\/code> in a  <a href=\"https:\/\/docs.adyen.com\/api-explorer\/Checkout\/latest\/post\/payments\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">\/payments<\/a> request:<\/p>\n<div data-component-wrapper=\"code-sample\">\n    <code-sample :title=\"''\" :id=\"'payments-idempotency'\" :code-data=\"[{&quot;language&quot;:&quot;bash&quot;,&quot;tabTitle&quot;:&quot;curl&quot;,&quot;content&quot;:&quot;curl https:\\\/\\\/checkout-test.adyen.com\\\/v72\\\/payments \\\\\\n-H 'x-api-key: {hint:Your API key from your Customer Area.}ADYEN_API_KEY{\\\/hint}' \\\\\\n-H 'idempotency-key: YOUR_IDEMPOTENCY_KEY' \\\\\\n-H 'content-type: application\\\/json' \\\\\\n-d '{\\n  \\&quot;merchantAccount\\&quot;: \\&quot;{hint:Name of your merchant account.}YOUR_MERCHANT_ACCOUNT{\\\/hint}\\&quot;,\\n  \\&quot;reference\\&quot;: \\&quot;My first Adyen test payment\\&quot;,\\n  \\&quot;amount\\&quot;: {\\n    \\&quot;value\\&quot;: {hint:10 euros in minor units}1000{\\\/hint},\\n    \\&quot;currency\\&quot;: \\&quot;EUR\\&quot;\\n  },\\n\\t\\&quot;paymentMethod\\&quot;: {\\n    \\&quot;type\\&quot;: \\&quot;scheme\\&quot;,\\n    \\&quot;encryptedCardNumber\\&quot;: \\&quot;test_4111111111111111\\&quot;,\\n    \\&quot;encryptedExpiryMonth\\&quot;: \\&quot;test_03\\&quot;,\\n    \\&quot;encryptedExpiryYear\\&quot;: \\&quot;test_2030\\&quot;,\\n    \\&quot;encryptedSecurityCode\\&quot;: \\&quot;test_737\\&quot;\\n  }\\n}'&quot;},{&quot;language&quot;:&quot;java&quot;,&quot;tabTitle&quot;:&quot;Java&quot;,&quot;content&quot;:&quot;\\\/\\\/ Import the required classes\\nimport com.adyen.Client;\\nimport com.adyen.enums.Environment;\\nimport com.adyen.service.checkout.PaymentsApi;\\nimport com.adyen.model.checkout.*;\\n\\n\\\/\\\/ Setup the client and service.\\nClient client = new Client(\\&quot;ADYEN_API_KEY\\&quot;, Environment.TEST);\\nPaymentsApi paymentsApi = new PaymentsApi(client);\\n\\n\\\/\\\/ Create a payment request.\\nPaymentRequest paymentRequest = new PaymentRequest();\\npaymentRequest.setMerchantAccount(\\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;);\\nCardDetails cardDetails = new CardDetails();\\ncardDetails.encryptedCardNumber(\\&quot;test_4111111111111111\\&quot;)\\n    .encryptedSecurityCode(\\&quot;test_737\\&quot;)\\n    .encryptedExpiryMonth(\\&quot;test_03\\&quot;)\\n    .encryptedExpiryYear(\\&quot;test_2030\\&quot;);\\npaymentRequest.setPaymentMethod(new CheckoutPaymentMethod(cardDetails));\\nAmount amount = new Amount().currency(\\&quot;EUR\\&quot;).value(1000L);\\npaymentRequest.setAmount(amount);\\npaymentRequest.setReference(\\&quot;My first Adyen test payment with an API library\\\/SDK\\&quot;);\\npaymentRequest.setReturnUrl(\\&quot;https:\\\/\\\/your-company.example.com\\\/checkout?shopperOrder=12xy..\\&quot;);\\n\\n\\\/\\\/ Add your idempotency key.\\nRequestOptions requestOptions = new RequestOptions();\\nrequestOptions.setIdempotencyKey(\\&quot;YOUR_IDEMPOTENCY_KEY\\&quot;);\\n\\n\\\/\\\/ Make a request to the \\\/payments endpoint.\\nPaymentResponse paymentResponse = paymentsApi.payments(paymentRequest, requestOptions);&quot;},{&quot;language&quot;:&quot;php&quot;,&quot;tabTitle&quot;:&quot;PHP&quot;,&quot;content&quot;:&quot;\\\/\\\/ Set up the client and service.\\n\\n$client = new \\\\Adyen\\\\Client();\\n\\n$client-&gt;setXApiKey(\\&quot;ADYEN_API_KEY\\&quot;);\\n$client-&gt;setEnvironment(\\\\Adyen\\\\Environment::TEST);\\n$client-&gt;setTimeout(30);\\n\\n$service = new \\\\Adyen\\\\Service\\\\Checkout\\\\PaymentsApi($client);\\n\\n$requestOptions['idempotencyKey'] = \\&quot;YOUR_IDEMPOTENCY_KEY\\&quot;;\\n\\n\\\/\\\/ Create a PaymentMethod object.\\n$paymentMethod = new CheckoutPaymentMethod();\\n$paymentMethod\\n    -&gt;setType(\\&quot;scheme\\&quot;)\\n    -&gt;setEncryptedCardNumber(\\&quot;test_4111111111111111\\&quot;)\\n    -&gt;setEncryptedExpiryMonth(\\&quot;test_03\\&quot;)\\n    -&gt;setEncryptedExpiryYear(\\&quot;test_2030\\&quot;)\\n    -&gt;setEncryptedSecurityCode(\\&quot;test_737\\&quot;);\\n\\n\\\/\\\/ Create an Amount object.\\n$amount = new Amount();\\n$amount\\n    -&gt;setValue(1500)\\n    -&gt;setCurrency(\\&quot;EUR\\&quot;);\\n\\n\\\/\\\/ Create the PaymentRequest object.\\n$paymentRequest = new PaymentRequest();\\n$paymentRequest\\n    -&gt;setMerchantAccount(\\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;)\\n    -&gt;setPaymentMethod($paymentMethod)\\n    -&gt;setAmount($amount)\\n    -&gt;setReference(\\&quot;My first Adyen test payment with an API library\\\/SDK\\&quot;)\\n    -&gt;setReturnUrl(\\&quot;https:\\\/\\\/your-company.example.com\\\/...\\&quot;);\\n\\n\\\/\\\/ Make a \\\/payments request.\\n$result = $service-&gt;payments($paymentRequest, $requestOptions);&quot;},{&quot;language&quot;:&quot;cs&quot;,&quot;tabTitle&quot;:&quot;C#&quot;,&quot;content&quot;:&quot;using Adyen;\\nusing Adyen.Model.Checkout;\\nusing Adyen.Service;\\nusing Environment = Adyen.Model.Environment;\\n\\n\\\/\\\/ Create a paymentRequest object.\\nvar amount = new Amount(\\&quot;USD\\&quot;, 1000);\\nvar paymentRequest = new PaymentRequest\\n{\\n    Reference = \\&quot;My first Adyen test payment with an API library\\\/SDK\\&quot;,\\n    Amount = amount,\\n    ReturnUrl = @\\&quot;https:\\\/\\\/your-company.example.com\\\/...\\&quot;,\\n    MerchantAccount = \\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;,\\n};\\n\\n\\\/\\\/ Set up the client and service.\\nvar config = new Config\\n{\\n    XApiKey = \\&quot;ADYEN_API_KEY\\&quot;,\\n    Environment = Environment.Test\\n};\\nvar client = new Client(config);\\nvar checkout = new PaymentsService(client);\\n\\n\\\/\\\/ Add your idempotency key.\\nvar requestOptions = new RequestOptions { IdempotencyKey= \\&quot;YOUR_IDEMPOTENCY_KEY\\&quot; };\\n\\n\\\/\\\/ Make a \\\/payments request.\\nvar paymentResponse = checkout.Payments(paymentRequest, requestOptions);&quot;},{&quot;language&quot;:&quot;js&quot;,&quot;tabTitle&quot;:&quot;NodeJS (JavaScript)&quot;,&quot;content&quot;:&quot;\\\/\\\/ Step 1: Require the parts of the module you want to use.\\nconst { Client, CheckoutAPI} = require('@adyen\\\/api-library');\\n\\n\\\/\\\/ Step 2: Initialize the client object.\\nconst client = new Client({apiKey: \\&quot;ADYEN_API_KEY\\&quot;, environment: \\&quot;TEST\\&quot;});\\n\\n\\\/\\\/ Step 3: Initialize the API object.\\nconst checkoutApi = new CheckoutAPI(client);\\n\\n\\\/\\\/ Step 4: Create the request object.\\nconst paymentRequest = {\\n    amount: {\\n      currency: \\&quot;USD\\&quot;,\\n      value: 1000 \\\/\\\/ value in minor units\\n    },\\n    reference: \\&quot;My first Adyen test payment with an API library\\\/SDK\\&quot;,\\n    paymentMethod: {\\n      type: \\&quot;scheme\\&quot;,\\n      encryptedCardNumber: \\&quot;test_4111111111111111\\&quot;,\\n      encryptedExpiryMonth: \\&quot;test_03\\&quot;,\\n      encryptedExpiryYear: \\&quot;test_2030\\&quot;,\\n      encryptedSecurityCode: \\&quot;test_737\\&quot;\\n    },\\n    shopperReference: \\&quot;YOUR_SHOPPER_REFERENCE\\&quot;,\\n    storePaymentMethod: true,\\n    shopperInteraction: \\&quot;Ecommerce\\&quot;,\\n    recurringProcessingModel: \\&quot;CardOnFile\\&quot;,\\n    returnUrl: \\&quot;https:\\\/\\\/your-company.example.com\\\/...\\&quot;,\\n    merchantAccount: \\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;\\n};\\n\\n\\\/\\\/ Optional: Add your idempotency key.\\nconst requestOptions = { \\&quot;idempotencyKey\\&quot;: \\&quot;YOUR_IDEMPOTENCY_KEY\\&quot; };\\n\\n\\\/\\\/ Step 5: Make a \\\/payments request.\\ncheckoutApi.PaymentsApi.payments(paymentRequest, requestOptions)\\n  .then(paymentResponse =&gt; console.log(paymentResponse.pspReference))\\n  .catch(error =&gt; console.log(error));&quot;},{&quot;language&quot;:&quot;ruby&quot;,&quot;tabTitle&quot;:&quot;Ruby&quot;,&quot;content&quot;:&quot;require 'adyen-ruby-api-library'\\n\\nadyen = Adyen::Client.new\\nadyen.api_key = \\&quot;ADYEN_API_KEY\\&quot;\\n\\nheaders = {\\&quot;idempotency-key\\&quot;: \\&quot;YOUR_IDEMPOTENCY_KEY\\&quot;}\\n\\nresponse = adyen.checkout.payments_api.payments({\\n  :amount =&gt; {\\n    :currency =&gt; \\&quot;EUR\\&quot;,\\n    :value =&gt; 1000\\n  },\\n  :reference =&gt; \\&quot;My first Adyen test payment with an API library\\\/SDK\\&quot;,\\n  :paymentMethod =&gt; {\\n    :type =&gt; \\&quot;scheme\\&quot;,\\n    :encryptedCardNumber =&gt; \\&quot;test_4111111111111111\\&quot;,\\n    :encryptedExpiryMonth =&gt; \\&quot;test_03\\&quot;,\\n    :encryptedExpiryYear =&gt; \\&quot;test_2030\\&quot;,\\n    :encryptedSecurityCode =&gt; \\&quot;test_737\\&quot;\\n  },\\n  :returnUrl =&gt; \\&quot;https:\\\/\\\/your-company.example.com\\\/checkout\\\/\\&quot;,\\n  :merchantAccount =&gt; \\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;\\n})&quot;}]\" :enable-copy-link-to-code-block=\"true\" :code-sample-card-size=\"'fullsize'\"><\/code-sample>\n<\/div>\n<h2 id=\"key-scope-and-validity-time\">Key scope and validity time<\/h2>\n<p>Keys are stored at a company account level. The system checks that the idempotency keys are unique to the company account.\u00a0Idempotency keys are valid for a minimum period of 7 days after first submission.<\/p>\n<div class=\"sc-notice note\"><div>\n<p>If you are targeting multiple regional endpoints simultaneously, idempotency keys will not be checked for duplication in other regions.<\/p>\n<\/div><\/div>\n<h2 id=\"security-considerations\">Security considerations<\/h2>\n<p>Generate unique idempotency keys per request using the <strong>version 4 (random)<\/strong> UUID type to prevent two API credentials under the same account from accessing each others responses.<\/p>\n<div class=\"sc-notice note\"><div>\n<p>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.<\/p>\n<\/div><\/div>\n<h2 id=\"error-handling\">Error handling<\/h2>\n<h3 id=\"transient-errors\">Transient errors<\/h3>\n<p>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.<\/p>\n<p>The response will contain an HTTP header\u00a0<span translate=\"no\"><strong>transient-error<\/strong><\/span> with the value <span translate=\"no\"><strong>true<\/strong><\/span>, 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\u00a0a 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\u00a0an\u00a0<span translate=\"no\"><strong>HTTP 422 \u2013 Unprocessable Entity<\/strong><\/span> or <span translate=\"no\"><strong>HTTP 409 - Conflict<\/strong><\/span> status with the error code <span translate=\"no\"><strong>704: \"request already processed or in progress\"<\/strong><\/span>.<\/p>\n<h3 id=\"retries-and-exponential-backoff\">Retries and exponential backoff<\/h3>\n<p>Use an exponential backoff strategy when retrying transactions to avoid flooding the API and running into rate-limiting. See\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Exponential_backoff\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">the Wikipedia article on exponential backoff<\/a>\u00a0for more information.<\/p>\n<h3 id=\"designing-for-resilience\">Designing for resilience<\/h3>\n<p>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\u00a0<span translate=\"no\"><strong>HTTP 503 \u2013 Service Unavailable<\/strong><\/span> status with the error code <span translate=\"no\"><strong>703: \"required resource temporarily unavailable\"<\/strong><\/span>. The system marks the request as a transient error (see above).<\/p>\n<p>If there are many of these responses, you can either:<\/p>\n<ul>\n<li>Pause processing and retry the requests later (if this is feasible).<\/li>\n<li>Fall back to non-idempotent processing by not submitting the <code>idempotency-key<\/code>\u00a0HTTP header.<\/li>\n<\/ul>","url":"https:\/\/docs.adyen.com\/development-resources\/api-idempotency","articleFields":{"description":"Learn how to safely resend API requests without performing the same operation multiple times.","id":"39952242","type":"page","_expandable":{"operations":""},"status":"current","last_edit_on":"30-07-2020 16:39","page_id":"eb0024dd-c9df-4115-afc2-e0003f84e11e","feedback_component":true,"filters_component":false,"decision_tree":"[]"},"algolia":{"url":"https:\/\/docs.adyen.com\/development-resources\/api-idempotency","title":"API idempotency","content":"The Adyen API supports\u00a0idempotency, allowing you to retry a request multiple times while only performing the action once.\u00a0This helps avoid unwanted duplication in case of failures and retries. For example, in the case of a timeout error,\u00a0it 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.\nThe accounting rules in the Adyen payments platform\u00a0take care of most potential double-processing issues that can impact\u00a0payment\u00a0modifications.\u00a0For example, the following default rules apply:\n\nCaptures: when partial captures are allowed, it is not possible to capture a higher amount than the authorized one.\nRefunds: when multiple refunds are allowed, by default the total refunded value cannot exceed the captured amount.\n\nTo minimize unwanted side effects when requests are duplicated, you can also take the following actions on your end:\n\nImplement asynchronous server-to-server webhooks. For example, this approach helps keep track of missing responses, a common consequence of a data transmission timeout.\nEnable 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).\n\nEnable idempotency\nTo submit a request for idempotent processing, send a request with the idempotency-key:&lt;key&gt; in the header.\nThe\u00a0&lt;key&gt;\u00a0is 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.\nTo verify that a request was processed idempotently, check the idempotency-key HTTP header returned in the response.\n\nMake sure that you code for case-insensitive HTTP headers.\n\nHere is an example of how to include the idempotency-key in a  \/payments request:\n\n    \n\nKey scope and validity time\nKeys are stored at a company account level. The system checks that the idempotency keys are unique to the company account.\u00a0Idempotency keys are valid for a minimum period of 7 days after first submission.\n\nIf you are targeting multiple regional endpoints simultaneously, idempotency keys will not be checked for duplication in other regions.\n\nSecurity considerations\nGenerate 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.\n\nLowering 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.\n\nError handling\nTransient errors\nIn 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.\nThe response will contain an HTTP header\u00a0transient-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\u00a0a 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\u00a0an\u00a0HTTP 422 \u2013 Unprocessable Entity or HTTP 409 - Conflict status with the error code 704: \"request already processed or in progress\".\nRetries and exponential backoff\nUse an exponential backoff strategy when retrying transactions to avoid flooding the API and running into rate-limiting. See\u00a0the Wikipedia article on exponential backoff\u00a0for more information.\nDesigning for resilience\nTo 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\u00a0HTTP 503 \u2013 Service Unavailable status with the error code 703: \"required resource temporarily unavailable\". The system marks the request as a transient error (see above).\nIf there are many of these responses, you can either:\n\nPause processing and retry the requests later (if this is feasible).\nFall back to non-idempotent processing by not submitting the idempotency-key\u00a0HTTP header.\n","type":"page","locale":"en","boost":18,"hierarchy":{"lvl0":"Home","lvl1":"Development resources","lvl2":"API idempotency"},"hierarchy_url":{"lvl0":"https:\/\/docs.adyen.com\/","lvl1":"https:\/\/docs.adyen.com\/development-resources","lvl2":"\/development-resources\/api-idempotency"},"levels":3,"category":"Development Resources","category_color":"green","tags":["idempotency"]},"articleFiles":{"payments-idempotency.js":"<p alt=\"\">payments-idempotency.js<\/p>"}}
