{"title":"Mutual authentication","category":"default","creationDate":1788433037,"content":"<p>Mutual authentication, also known as two-way authentication, uses a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Client_certificate\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">client certificate<\/a> in addition to the server certificate. Adyen verifies your certificate on every API request. This adds security on top of your API key.<\/p>\n<p>Adyen acts as a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Certificate_authority\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">certificate authority<\/a> (CA) and issues client certificates. You send a certificate signing request (CSR) to the Management API. Adyen returns a signed certificate. You then link the certificate to an API credential. Your private key never leaves your system.<\/p>\n<p>Adyen recommends that you enroll your client certificates with Adyen instead of another certificate authority. Enrollment with Adyen gives you faster onboarding, more control over your certificates, and faster support. If you must use another certificate authority, see <a href=\"#external-ca\">Use a certificate from another certificate authority<\/a>.<\/p>\n<h2>Requirements<\/h2>\n<p>Before you begin, take into account the following requirements, limitations, and preparations.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">Requirement<\/th>\n<th style=\"text-align: left;\">Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\"><strong><a href=\"\/development-resources\/api-credentials\/roles\/\">API credential roles<\/a><\/strong><\/td>\n<td style=\"text-align: left;\">Make sure that your API credential has the following <a href=\"\/development-resources\/api-credentials\/roles\/#management-api\">roles<\/a>: <ul><li markdown=\"1\"><strong>Management API - Client certificates read<\/strong> to get your certificates.<\/li><li markdown=\"1\"><strong>Management API - Client certificates read and write<\/strong> to generate a certificate.<\/li><\/ul><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><strong>Setup steps<\/strong><\/td>\n<td style=\"text-align: left;\">Before you begin: <ul><li markdown=\"1\"><a href=\"\/development-resources\/api-credentials\/#generate-api-key\">Generate an API key<\/a>.<\/li><li markdown=\"1\">Install <a href=\"https:\/\/www.openssl.org\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">OpenSSL<\/a> or another tool that can create a certificate signing request.<\/li><li markdown=\"1\">Install <a href=\"https:\/\/jqlang.github.io\/jq\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">jq<\/a>, or another tool that can parse JSON, to read the enrollment response.<\/li><\/ul><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><strong>Limitations<\/strong><\/td>\n<td style=\"text-align: left;\"><ul><li markdown=\"1\">You can have a maximum of 10 valid client certificates per company account.<\/li><li markdown=\"1\">Each certificate is valid for 397 days.<\/li><li markdown=\"1\">You can only use a company-level API credential, in the format: <code>ws_*@Company.*<\/code>. Merchant-level and balance platform API credentials are not supported.<\/li><li markdown=\"1\">Only some APIs support mutual authentication. For the full list, see <a href=\"#supported-apis\">Supported APIs<\/a>.<\/li><\/ul><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>How it works<\/h2>\n<p>To set up mutual authentication:<\/p>\n<ol>\n<li>You create a private key and a certificate signing request.<\/li>\n<li>You send the certificate signing request to Adyen.<\/li>\n<li>Adyen signs the request and returns your client certificate and certificate chain.<\/li>\n<li>You link the subject DN of the certificate to an API credential.<\/li>\n<li>You send API requests to a <a href=\"#mutual-authentication-endpoints\">mutual authentication endpoint<\/a> with your certificate.<\/li>\n<\/ol>\n<p>Adyen accepts the request when the subject DN of the certificate matches the subject DN stored for the API credential.<\/p>\n<h2>Generate a client certificate<\/h2>\n<p>To generate a client certificate:<\/p>\n<ol>\n<li>\n<p>Create a private key and a certificate signing request:<\/p>\n<pre><code class=\"language-bash\">openssl req -new \\\n -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \\\n -keyout client.key \\\n -out client.csr \\\n -subj \"\/CN=adyen-mtls-client\"<\/code><\/pre>\n<p>OpenSSL prompts for a passphrase and encrypts <code>client.key<\/code> with it. Keep both the passphrase and <code>client.key<\/code> secret. You cannot get <code>client.key<\/code> from Adyen if you lose it.<\/p>\n<p>Adyen does not use the subject in your certificate signing request. Adyen builds the <code>subjectDN<\/code> of the signed certificate from the company account in the request path and the <code>apiCredential<\/code> in the request body, in the format:<\/p>\n<pre><code class=\"language-text\">UID={apiCredential}-Company.{companyId},CN={apiCredential}-Company.{companyId},O=Company.{companyId}<\/code><\/pre>\n<p>Use any subject in the <code>-subj<\/code> value.<\/p>\n<\/li>\n<li>\n<p>Convert the certificate signing request to a Base64-encoded DER string:<\/p>\n<pre><code class=\"language-bash\">openssl req -in client.csr -outform DER | openssl base64 -A<\/code><\/pre>\n<\/li>\n<li>\n<p>Make a POST <code>\/companies\/{companyId}\/clientCertificates<\/code> request. In the request, include:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">Parameter<\/th>\n<th style=\"text-align: left;\">Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\"><code>apiCredential<\/code><\/td>\n<td style=\"text-align: left;\">The API credential that you want to use the certificate with, in the format: <code>ws_*@Company.*<\/code>. The credential must belong to the company account in the request path. If you send a merchant-level credential, or a credential from another company account, you get a 403 error.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><code>certificateSigningRequest<\/code><\/td>\n<td style=\"text-align: left;\">The certificate signing request from the previous step, as a Base64-encoded DER string.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre><code class=\"language-curl\">curl https:\/\/management-live.adyen.com\/v3\/companies\/YOUR_COMPANY_ACCOUNT\/clientCertificates \\\n-X POST \\\n-H 'x-API-key: ADYEN_API_KEY' \\\n-H 'content-type: application\/json' \\\n-d '{\n  \"apiCredential\": \"ws_123456@Company.YourCompanyAccount\",\n  \"certificateSigningRequest\": \"YOUR_BASE64_ENCODED_CSR\"\n}'<\/code><\/pre>\n<div class=\"notices green\">\n<p>To try this out first, send the same request to <span translate=\"no\"><strong>management-test.adyen.com<\/strong><\/span>. Certificates that you generate in the test environment work only with the test <a href=\"#mutual-authentication-endpoints\">mutual authentication endpoints<\/a>.<\/p>\n<\/div>\n<\/li>\n<li>\n<p>Save the response to a file, for example <code>enrollment.json<\/code>. Note the following:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">Parameter<\/th>\n<th style=\"text-align: left;\">Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\"><code>leaf<\/code><\/td>\n<td style=\"text-align: left;\">Your signed client certificate, as a Base64-encoded DER string.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><code>caChain<\/code><\/td>\n<td style=\"text-align: left;\">The certificate chain, including intermediate and root certificates. This does not include your certificate.<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><code>subjectDN<\/code><\/td>\n<td style=\"text-align: left;\">The subject DN of your certificate. You need this in the next step.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/li>\n<li>\n<p>Assemble a PEM file that contains your <code>leaf<\/code> certificate first, followed by each <code>caChain<\/code> entry in the order that the response returns them. This is the certificate bundle you present on every request.<\/p>\n<p>From the saved <code>enrollment.json<\/code> file, decode the <code>leaf<\/code> and each <code>caChain<\/code> entry from Base64 DER into PEM. Write the <code>leaf<\/code> and <code>caChain<\/code> entries to a <code>client.crt<\/code> file:<\/p>\n<pre><code class=\"language-bash\">jq -r '.leaf, .caChain[]' enrollment.json \\\n | while read -r cert; do \\\n     printf '%s' \"$cert\" | openssl base64 -d -A | openssl x509 -inform DER; \\\n   done &gt; client.crt<\/code><\/pre>\n<p>Use <code>client.crt<\/code>, together with your private key, as the client certificate on every <a href=\"#make-request-with-mtls\">mTLS request<\/a>.<\/p>\n<p>Optionally, verify the bundle:<\/p>\n<pre><code class=\"language-bash\">openssl crl2pkcs7 -nocrl -certfile client.crt | openssl pkcs7 -print_certs -noout<\/code><\/pre>\n<p>The output shows the <code>leaf<\/code> first, followed by each CA in the chain up to the root.<\/p>\n<\/li>\n<\/ol>\n<div class=\"notices yellow\">\n<p>Send your certificate and the chain together. If you send only your certificate, the TLS handshake fails with an <span translate=\"no\"><strong>unknown_ca<\/strong><\/span> alert, and you get no HTTP response.<\/p>\n<\/div>\n<p>To get the certificates that you already have, make a GET <code>\/companies\/{companyId}\/clientCertificates<\/code> request. The response includes only the certificates that are neither expired nor revoked.<\/p>\n<h2>Link the certificate to your API credential<\/h2>\n<p>Adyen matches the certificate you present against the subject DN stored for your API credential. Link them in your Customer Area or with the Management API.<\/p>\n\n<div id=\"tabMyOYQ\">\n    <div data-component-wrapper=\"tabs\">\n        <tabs\n                        :items=\"[{&quot;title&quot;:&quot;Customer Area&quot;,&quot;content&quot;:&quot;\\n&lt;ol&gt;\\n&lt;li&gt;\\n&lt;p&gt;Log in to your &lt;a href=\\&quot;https:\\\/\\\/ca-live.adyen.com\\\/\\&quot; target=\\&quot;_blank\\&quot; rel=\\&quot;nofollow noopener noreferrer\\&quot; class=\\&quot;external-link no-image\\&quot;&gt;Customer Area&lt;\\\/a&gt;.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Go to &lt;strong&gt;Developers&lt;\\\/strong&gt; &amp;gt; &lt;strong&gt;API credentials&lt;\\\/strong&gt;, and select the API credential that you used in the &lt;code&gt;apiCredential&lt;\\\/code&gt; field.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Under &lt;strong&gt;Authentication&lt;\\\/strong&gt;, in the &lt;strong&gt;Client Certificate (DN)&lt;\\\/strong&gt; field, enter the &lt;code&gt;subjectDN&lt;\\\/code&gt; from the response.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Select &lt;strong&gt;Save&lt;\\\/strong&gt;.&lt;\\\/p&gt;\\n&quot;,&quot;altTitle&quot;:null,&quot;oldTabId&quot;:1,&quot;relation&quot;:&quot;&quot;},{&quot;title&quot;:&quot;Management API&quot;,&quot;content&quot;:&quot;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Get the ID of your API credential. Make a GET  &lt;a href=\\&quot;https:\\\/\\\/docs.adyen.com\\\/api-explorer\\\/Management\\\/latest\\\/get\\\/companies\\\/(companyId)\\\/apiCredentials\\&quot; class=\\&quot;codeLabel  external-link no-image\\&quot; target=\\&quot;_blank\\&quot; rel=\\&quot;nofollow noopener noreferrer\\&quot;&gt;\\\/companies\\\/{companyId}\\\/apiCredentials&lt;\\\/a&gt; request, then find the credential whose &lt;code&gt;username&lt;\\\/code&gt; matches the &lt;code&gt;apiCredential&lt;\\\/code&gt; that you enrolled, and copy its &lt;code&gt;id&lt;\\\/code&gt;.&lt;\\\/p&gt;\\n&lt;div class=\\&quot;notices green\\&quot;&gt;\\n&lt;p&gt;The enroll request identifies your credential by username, but this request identifies it by ID. These are different values for the same credential.&lt;\\\/p&gt;\\n&lt;\\\/div&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Make a PATCH  &lt;a href=\\&quot;https:\\\/\\\/docs.adyen.com\\\/api-explorer\\\/Management\\\/latest\\\/patch\\\/companies\\\/(companyId)\\\/apiCredentials\\\/(apiCredentialId)\\&quot; class=\\&quot;codeLabel  external-link no-image\\&quot; target=\\&quot;_blank\\&quot; rel=\\&quot;nofollow noopener noreferrer\\&quot;&gt;\\\/companies\\\/{companyId}\\\/apiCredentials\\\/{apiCredentialId}&lt;\\\/a&gt; request, and set  &lt;a href=\\&quot;https:\\\/\\\/docs.adyen.com\\\/api-explorer\\\/Management\\\/latest\\\/patch\\\/companies\\\/(companyId)\\\/apiCredentials\\\/(apiCredentialId)#request-subjectDN\\&quot; class=\\&quot;codeLabel  external-link no-image\\&quot; target=\\&quot;_blank\\&quot; rel=\\&quot;nofollow noopener noreferrer\\&quot;&gt;subjectDN&lt;\\\/a&gt; to the &lt;code&gt;subjectDN&lt;\\\/code&gt; from the response.&lt;\\\/p&gt;\\n&lt;pre&gt;&lt;code class=\\&quot;language-curl\\&quot;&gt;curl https:\\\/\\\/management-live.adyen.com\\\/v3\\\/companies\\\/YOUR_COMPANY_ACCOUNT\\\/apiCredentials\\\/YOUR_API_CREDENTIAL_ID \\\\\\n-X PATCH \\\\\\n-H &#039;x-API-key: ADYEN_API_KEY&#039; \\\\\\n-H &#039;content-type: application\\\/json&#039; \\\\\\n-d &#039;{\\n  \\&quot;subjectDN\\&quot;: \\&quot;SUBJECT_DN_FROM_RESPONSE\\&quot;\\n}&#039;&lt;\\\/code&gt;&lt;\\\/pre&gt;\\n&quot;,&quot;altTitle&quot;:null,&quot;oldTabId&quot;:1,&quot;relation&quot;:&quot;&quot;}]\"\n            :should-update-when-url-changes='false'>\n        <\/tabs>\n    <\/div>\n<\/div>\n\n<\/li>\n<\/ol>\n<div class=\"notices yellow\">\n<p>After you link a certificate, every request that uses this API credential to access an API that supports mutual authentication must present the certificate. Requests to those APIs without it fail. Make sure your integration is ready before you save.<\/p>\n<\/div>\n<h2>Test your setup<\/h2>\n<p>Before you link a certificate to a live API credential, test the same process in the test environment.<\/p>\n<p>To test your setup:<\/p>\n<ol>\n<li>\n<p>Generate a certificate with <code>https:\/\/management-test.adyen.com<\/code>.<\/p>\n<\/li>\n<li>\n<p>Link the certificate to a test company-level API credential.<\/p>\n<\/li>\n<li>\n<p>Send a request to a test mutual authentication endpoint with your certificate and private key:<\/p>\n<pre><code class=\"language-curl\">curl https:\/\/checkoutcert-test.adyen.com\/v72\/paymentMethods \\\n-X POST \\\n--cert client.crt \\\n--key client.key \\\n-H 'x-API-key: ADYEN_API_KEY' \\\n-H 'content-type: application\/json' \\\n-d '{\n  \"merchantAccount\": \"YOUR_MERCHANT_ACCOUNT\",\n  \"countryCode\": \"NL\",\n  \"amount\": {\n     \"currency\": \"EUR\",\n     \"value\": 1000\n  }\n}'<\/code><\/pre>\n<div class=\"notices green\">\n<p>Because <code>client.key<\/code> is encrypted, curl prompts for the passphrase. To send the request without a prompt, add <code>--pass YOUR_KEY_PASSPHRASE<\/code>.<\/p>\n<\/div>\n<\/li>\n<li>\n<p>Check that the request returns an HTTP response from Adyen.<\/p>\n<\/li>\n<\/ol>\n<p>If the TLS handshake fails before you get an HTTP response, check that <code>client.crt<\/code> contains your certificate first, followed by the full chain.<\/p>\n<h2 id=\"supported-apis\">Supported APIs<\/h2>\n<p>After you link a certificate, you must send requests to the following APIs over a mutual authentication connection with your client certificate:<\/p>\n<ul>\n<li>\n <a href=\"https:\/\/docs.adyen.com\/api-explorer\/Checkout\/latest\/overview\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Checkout<\/a>\n<\/li>\n<li><a href=\"https:\/\/docs.adyen.com\/api-explorer\/Payment\/latest\/overview\" class=\"codeLabel external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Payment<\/a> (classic integration)<\/li>\n<li>\n <a href=\"https:\/\/docs.adyen.com\/api-explorer\/Recurring\/latest\/overview\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Recurring<\/a>\n<\/li>\n<li>\n <a href=\"https:\/\/docs.adyen.com\/api-explorer\/Payout\/latest\/overview\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Payout<\/a>\n<\/li>\n<li>\n <a href=\"https:\/\/docs.adyen.com\/api-explorer\/BinLookup\/latest\/overview\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">BinLookup<\/a>\n<\/li>\n<\/ul>\n<p>Other APIs do not support mutual authentication. These include the Management API, the Balance Platform APIs, the Transfers API, the Legal Entity Management API, and the Terminal Management API. Requests to those APIs use your API key only, and you send them to their standard endpoints.<\/p>\n<h2 id=\"mutual-authentication-endpoints\">Endpoints for mutual authentication<\/h2>\n<p>When you send an API request, use the endpoint that supports mutual authentication.<\/p>\n<p>For Checkout API:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">Environment<\/th>\n<th style=\"text-align: left;\">Region<\/th>\n<th style=\"text-align: left;\">Mutual authentication endpoint<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">Test<\/td>\n<td style=\"text-align: left;\">All<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>checkoutcert-test.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">Live<\/td>\n<td style=\"text-align: left;\">EU<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>checkoutcert-live-eu.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">Live<\/td>\n<td style=\"text-align: left;\">AU<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>checkoutcert-live-au.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">Live<\/td>\n<td style=\"text-align: left;\">US<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>checkoutcert-live-us.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the live environment, the Checkout mutual authentication endpoint is regional. Do not use the <code>{PREFIX}-checkout-live.adyenpayments.com<\/code> endpoint that you use for API key authentication.<\/p>\n<p>For Payment, Recurring, Payout, and BinLookup APIs:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">Environment<\/th>\n<th style=\"text-align: left;\">Region<\/th>\n<th style=\"text-align: left;\">Mutual authentication endpoint<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">Test<\/td>\n<td style=\"text-align: left;\">All<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>palcert-test.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\">Live<\/td>\n<td style=\"text-align: left;\">All<\/td>\n<td style=\"text-align: left;\"><span translate=\"no\"><strong>palcert-live.adyen.com<\/strong><\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For example, to send a  <a href=\"https:\/\/docs.adyen.com\/api-explorer\/Checkout\/latest\/post\/paymentMethods\" class=\"codeLabel  external-link no-image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">\/paymentMethods<\/a> request with mutual authentication in the EU region, use <code>https:\/\/checkoutcert-live-eu.adyen.com\/v72\/paymentMethods<\/code>.<\/p>\n<h2 id=\"make-request-with-mtls\">Make a request with your client certificate<\/h2>\n\n<div id=\"tabyE5dO\">\n    <div data-component-wrapper=\"tabs\">\n        <tabs\n                        :items=\"[{&quot;title&quot;:&quot;curl&quot;,&quot;content&quot;:&quot;\\n&lt;p&gt;Send an API request as you normally do. For example, &lt;a href=\\&quot;\\\/online-payments\\\/build-your-integration\\\/advanced-flow?platform=Web&amp;amp;integration=API%20only#get-available-payment-methods\\&quot;&gt;get the available payment methods&lt;\\\/a&gt;. Make sure that you:&lt;\\\/p&gt;\\n&lt;ol&gt;\\n&lt;li&gt;Pass &lt;code&gt;cert&lt;\\\/code&gt; and &lt;code&gt;key&lt;\\\/code&gt;. The &lt;code&gt;cert&lt;\\\/code&gt; file must contain your certificate and the full chain.&lt;\\\/li&gt;\\n&lt;li&gt;Use the correct &lt;a href=\\&quot;#mutual-authentication-endpoints\\&quot;&gt;endpoint that supports mutual authentication&lt;\\\/a&gt;.&lt;\\\/li&gt;\\n&lt;\\\/ol&gt;\\n&lt;pre&gt;&lt;code class=\\&quot;language-curl\\&quot;&gt;curl https:\\\/\\\/checkoutcert-live-eu.adyen.com\\\/v72\\\/paymentMethods \\\\\\n-X POST \\\\\\n--cert client.crt \\\\\\n--key client.key \\\\\\n-H &#039;x-API-key: ADYEN_API_KEY&#039; \\\\\\n-H &#039;content-type: application\\\/json&#039; \\\\\\n-d &#039;{\\n   \\&quot;merchantAccount\\&quot;: \\&quot;YOUR_MERCHANT_ACCOUNT\\&quot;,\\n   \\&quot;countryCode\\&quot;: \\&quot;NL\\&quot;,\\n   \\&quot;amount\\&quot;: {\\n      \\&quot;currency\\&quot;: \\&quot;EUR\\&quot;,\\n      \\&quot;value\\&quot;: 1000\\n   }\\n}&#039;&lt;\\\/code&gt;&lt;\\\/pre&gt;\\n&lt;div class=\\&quot;notices green\\&quot;&gt;\\n&lt;p&gt;Because &lt;code&gt;client.key&lt;\\\/code&gt; is encrypted, curl prompts for the passphrase. To send the request without a prompt, add &lt;code&gt;--pass YOUR_KEY_PASSPHRASE&lt;\\\/code&gt;.&lt;\\\/p&gt;\\n&lt;\\\/div&gt;\\n&quot;,&quot;altTitle&quot;:null,&quot;oldTabId&quot;:1,&quot;relation&quot;:&quot;&quot;},{&quot;title&quot;:&quot;Java&quot;,&quot;content&quot;:&quot;\\n&lt;div class=\\&quot;notices green\\&quot;&gt;\\n&lt;p&gt;Our Java API library supports mutual authentication. You can find the &lt;a href=\\&quot;https:\\\/\\\/github.com\\\/Adyen\\\/adyen-java-api-library\\&quot; target=\\&quot;_blank\\&quot; rel=\\&quot;nofollow noopener noreferrer\\&quot; class=\\&quot;external-link no-image\\&quot;&gt;latest version on GitHub&lt;\\\/a&gt;.&lt;\\\/p&gt;\\n&lt;\\\/div&gt;\\n&lt;p&gt;To use mutual authentication when you send an API request with our Java API library:&lt;\\\/p&gt;\\n&lt;ol&gt;\\n&lt;li&gt;\\n&lt;p&gt;Follow the instructions to &lt;a href=\\&quot;\\\/development-resources\\\/libraries\\&quot;&gt;install the Java API library&lt;\\\/a&gt;, if you have not done so already.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Find the &lt;code&gt;Client&lt;\\\/code&gt; constructor line:&lt;\\\/p&gt;\\n&lt;pre&gt;&lt;code class=\\&quot;language-java\\&quot;&gt;Client client = new Client(xApiKey, Environment.LIVE);&lt;\\\/code&gt;&lt;\\\/pre&gt;\\n&lt;p&gt;Replace it with the following code:&lt;\\\/p&gt;\\n&lt;pre&gt;&lt;code class=\\&quot;language-java\\&quot;&gt;KeyStore trustStore = CertificateUtil.loadKeyStore(\\&quot;TRUST_STORE_PATH\\&quot;, \\&quot;KEY_TYPE\\&quot;, \\&quot;TRUST_STORE_PASSWORD\\&quot;);\\nKeyStore keyStore = CertificateUtil.loadKeyStore(\\&quot;CLIENT_STORE_PATH\\&quot;, \\&quot;KEY_TYPE\\&quot;, \\&quot;KEYSTORE_PASSWORD\\&quot;);\\n\\nKeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());\\nkeyManagerFactory.init(keyStore, \\&quot;KEYSTORE_PASSWORD\\&quot;.toCharArray());\\n\\nTrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\\ntrustManagerFactory.init(trustStore);\\n\\nSSLContext sslContext = SSLContext.getInstance(\\&quot;TLS\\&quot;);\\nsslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);\\n\\nString apiKey = \\&quot;ADYEN_API_KEY\\&quot;;\\nClient client = new Client(sslContext, apiKey);&lt;\\\/code&gt;&lt;\\\/pre&gt;\\n&lt;p&gt;This code configures the library for the live environment and requires the following imports: &lt;\\\/p&gt;\\n&lt;ul&gt;\\n&lt;li&gt;&lt;code&gt;com.adyen.util.CertificateUtil&lt;\\\/code&gt; &lt;\\\/li&gt;\\n&lt;li&gt;&lt;code&gt;java.security.KeyStore&lt;\\\/code&gt; &lt;\\\/li&gt;\\n&lt;li&gt;&lt;code&gt;javax.net.ssl.KeyManagerFactory&lt;\\\/code&gt; &lt;\\\/li&gt;\\n&lt;li&gt;&lt;code&gt;javax.net.ssl.SSLContext&lt;\\\/code&gt; &lt;\\\/li&gt;\\n&lt;li&gt;&lt;code&gt;javax.net.ssl.TrustManagerFactory&lt;\\\/code&gt;.&lt;\\\/li&gt;\\n&lt;\\\/ul&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Replace &lt;code&gt;TRUST_STORE_PATH&lt;\\\/code&gt;, &lt;code&gt;CLIENT_STORE_PATH&lt;\\\/code&gt;, &lt;code&gt;TRUST_STORE_PASSWORD&lt;\\\/code&gt;, &lt;code&gt;KEYSTORE_PASSWORD&lt;\\\/code&gt;, &lt;code&gt;KEY_TYPE&lt;\\\/code&gt;, and &lt;code&gt;ADYEN_API_KEY&lt;\\\/code&gt; with your values.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;li&gt;\\n&lt;p&gt;Use the correct &lt;a href=\\&quot;#mutual-authentication-endpoints\\&quot;&gt;endpoint that supports mutual authentication&lt;\\\/a&gt;.&lt;\\\/p&gt;\\n&lt;\\\/li&gt;\\n&lt;\\\/ol&gt;\\n&quot;,&quot;altTitle&quot;:null,&quot;oldTabId&quot;:1,&quot;relation&quot;:&quot;&quot;}]\"\n            :should-update-when-url-changes='false'>\n        <\/tabs>\n    <\/div>\n<\/div>\n\n<h2 id=\"external-ca\">Use a certificate from another certificate authority<\/h2>\n<p>If you must use a certificate that is signed by another certificate authority, Adyen requires that:<\/p>\n<ul>\n<li>A <a href=\"https:\/\/en.wikipedia.org\/wiki\/Certificate_authority\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">certificate authority<\/a> that Adyen trusts signs the certificate.<\/li>\n<li>The subject DN of the certificate matches the subject DN stored for the <a href=\"\/development-resources\/api-credentials\">API credential<\/a>.<\/li>\n<\/ul>\n<p>After you get your certificate, <a href=\"#link-the-certificate-to-your-api-credential\">link it to your API credential<\/a>. If you are not sure whether Adyen trusts the certificate authority, contact our <a href=\"https:\/\/ca-test.adyen.com\/ca\/ca\/contactUs\/support.shtml?form=other\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" class=\"external-link no-image\">Support Team<\/a>.<\/p>\n<h2>See also<\/h2>\n<div class=\"see-also-links output-inline\" id=\"see-also\">\n<ul><li><a href=\"\/development-resources\/api-credentials\"\n                        target=\"_self\"\n                        >\n                    API credentials\n                <\/a><\/li><li><a href=\"\/development-resources\/api-credentials\/roles\"\n                        target=\"_self\"\n                        >\n                    API credential roles\n                <\/a><\/li><li><a href=\"\/development-resources\/live-endpoints\"\n                        target=\"_self\"\n                        >\n                    Live endpoints\n                <\/a><\/li><li><a href=\"\/development-resources\/api-authentication\"\n                        target=\"_self\"\n                        >\n                    API authentication\n                <\/a><\/li><\/ul><\/div>\n","url":"https:\/\/docs.adyen.com\/development-resources\/mtls","articleFields":{"description":"Use a client certificate to verify the identity of the client when you send API requests to Adyen.","feedback_component":true,"robots":"noindex,nofollow","last_edit_on":"30-07-2026 12:00"},"algolia":{"url":"https:\/\/docs.adyen.com\/development-resources\/mtls","title":"Mutual authentication","content":"Mutual authentication, also known as two-way authentication, uses a client certificate in addition to the server certificate. Adyen verifies your certificate on every API request. This adds security on top of your API key.\nAdyen acts as a certificate authority (CA) and issues client certificates. You send a certificate signing request (CSR) to the Management API. Adyen returns a signed certificate. You then link the certificate to an API credential. Your private key never leaves your system.\nAdyen recommends that you enroll your client certificates with Adyen instead of another certificate authority. Enrollment with Adyen gives you faster onboarding, more control over your certificates, and faster support. If you must use another certificate authority, see Use a certificate from another certificate authority.\nRequirements\nBefore you begin, take into account the following requirements, limitations, and preparations.\n\n\n\nRequirement\nDescription\n\n\n\n\nAPI credential roles\nMake sure that your API credential has the following roles: Management API - Client certificates read to get your certificates.Management API - Client certificates read and write to generate a certificate.\n\n\nSetup steps\nBefore you begin: Generate an API key.Install OpenSSL or another tool that can create a certificate signing request.Install jq, or another tool that can parse JSON, to read the enrollment response.\n\n\nLimitations\nYou can have a maximum of 10 valid client certificates per company account.Each certificate is valid for 397 days.You can only use a company-level API credential, in the format: ws_*@Company.*. Merchant-level and balance platform API credentials are not supported.Only some APIs support mutual authentication. For the full list, see Supported APIs.\n\n\n\nHow it works\nTo set up mutual authentication:\n\nYou create a private key and a certificate signing request.\nYou send the certificate signing request to Adyen.\nAdyen signs the request and returns your client certificate and certificate chain.\nYou link the subject DN of the certificate to an API credential.\nYou send API requests to a mutual authentication endpoint with your certificate.\n\nAdyen accepts the request when the subject DN of the certificate matches the subject DN stored for the API credential.\nGenerate a client certificate\nTo generate a client certificate:\n\n\nCreate a private key and a certificate signing request:\nopenssl req -new \\\n -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \\\n -keyout client.key \\\n -out client.csr \\\n -subj \"\/CN=adyen-mtls-client\"\nOpenSSL prompts for a passphrase and encrypts client.key with it. Keep both the passphrase and client.key secret. You cannot get client.key from Adyen if you lose it.\nAdyen does not use the subject in your certificate signing request. Adyen builds the subjectDN of the signed certificate from the company account in the request path and the apiCredential in the request body, in the format:\nUID={apiCredential}-Company.{companyId},CN={apiCredential}-Company.{companyId},O=Company.{companyId}\nUse any subject in the -subj value.\n\n\nConvert the certificate signing request to a Base64-encoded DER string:\nopenssl req -in client.csr -outform DER | openssl base64 -A\n\n\nMake a POST \/companies\/{companyId}\/clientCertificates request. In the request, include:\n\n\n\nParameter\nDescription\n\n\n\n\napiCredential\nThe API credential that you want to use the certificate with, in the format: ws_*@Company.*. The credential must belong to the company account in the request path. If you send a merchant-level credential, or a credential from another company account, you get a 403 error.\n\n\ncertificateSigningRequest\nThe certificate signing request from the previous step, as a Base64-encoded DER string.\n\n\n\ncurl https:\/\/management-live.adyen.com\/v3\/companies\/YOUR_COMPANY_ACCOUNT\/clientCertificates \\\n-X POST \\\n-H 'x-API-key: ADYEN_API_KEY' \\\n-H 'content-type: application\/json' \\\n-d '{\n  \"apiCredential\": \"ws_123456@Company.YourCompanyAccount\",\n  \"certificateSigningRequest\": \"YOUR_BASE64_ENCODED_CSR\"\n}'\n\nTo try this out first, send the same request to management-test.adyen.com. Certificates that you generate in the test environment work only with the test mutual authentication endpoints.\n\n\n\nSave the response to a file, for example enrollment.json. Note the following:\n\n\n\nParameter\nDescription\n\n\n\n\nleaf\nYour signed client certificate, as a Base64-encoded DER string.\n\n\ncaChain\nThe certificate chain, including intermediate and root certificates. This does not include your certificate.\n\n\nsubjectDN\nThe subject DN of your certificate. You need this in the next step.\n\n\n\n\n\nAssemble a PEM file that contains your leaf certificate first, followed by each caChain entry in the order that the response returns them. This is the certificate bundle you present on every request.\nFrom the saved enrollment.json file, decode the leaf and each caChain entry from Base64 DER into PEM. Write the leaf and caChain entries to a client.crt file:\njq -r '.leaf, .caChain[]' enrollment.json \\\n | while read -r cert; do \\\n     printf '%s' \"$cert\" | openssl base64 -d -A | openssl x509 -inform DER; \\\n   done &gt; client.crt\nUse client.crt, together with your private key, as the client certificate on every mTLS request.\nOptionally, verify the bundle:\nopenssl crl2pkcs7 -nocrl -certfile client.crt | openssl pkcs7 -print_certs -noout\nThe output shows the leaf first, followed by each CA in the chain up to the root.\n\n\n\nSend your certificate and the chain together. If you send only your certificate, the TLS handshake fails with an unknown_ca alert, and you get no HTTP response.\n\nTo get the certificates that you already have, make a GET \/companies\/{companyId}\/clientCertificates request. The response includes only the certificates that are neither expired nor revoked.\nLink the certificate to your API credential\nAdyen matches the certificate you present against the subject DN stored for your API credential. Link them in your Customer Area or with the Management API.\n\n\n    \n        \n        \n    \n\n\n\n\n\nAfter you link a certificate, every request that uses this API credential to access an API that supports mutual authentication must present the certificate. Requests to those APIs without it fail. Make sure your integration is ready before you save.\n\nTest your setup\nBefore you link a certificate to a live API credential, test the same process in the test environment.\nTo test your setup:\n\n\nGenerate a certificate with https:\/\/management-test.adyen.com.\n\n\nLink the certificate to a test company-level API credential.\n\n\nSend a request to a test mutual authentication endpoint with your certificate and private key:\ncurl https:\/\/checkoutcert-test.adyen.com\/v72\/paymentMethods \\\n-X POST \\\n--cert client.crt \\\n--key client.key \\\n-H 'x-API-key: ADYEN_API_KEY' \\\n-H 'content-type: application\/json' \\\n-d '{\n  \"merchantAccount\": \"YOUR_MERCHANT_ACCOUNT\",\n  \"countryCode\": \"NL\",\n  \"amount\": {\n     \"currency\": \"EUR\",\n     \"value\": 1000\n  }\n}'\n\nBecause client.key is encrypted, curl prompts for the passphrase. To send the request without a prompt, add --pass YOUR_KEY_PASSPHRASE.\n\n\n\nCheck that the request returns an HTTP response from Adyen.\n\n\nIf the TLS handshake fails before you get an HTTP response, check that client.crt contains your certificate first, followed by the full chain.\nSupported APIs\nAfter you link a certificate, you must send requests to the following APIs over a mutual authentication connection with your client certificate:\n\n\n Checkout\n\nPayment (classic integration)\n\n Recurring\n\n\n Payout\n\n\n BinLookup\n\n\nOther APIs do not support mutual authentication. These include the Management API, the Balance Platform APIs, the Transfers API, the Legal Entity Management API, and the Terminal Management API. Requests to those APIs use your API key only, and you send them to their standard endpoints.\nEndpoints for mutual authentication\nWhen you send an API request, use the endpoint that supports mutual authentication.\nFor Checkout API:\n\n\n\nEnvironment\nRegion\nMutual authentication endpoint\n\n\n\n\nTest\nAll\ncheckoutcert-test.adyen.com\n\n\nLive\nEU\ncheckoutcert-live-eu.adyen.com\n\n\nLive\nAU\ncheckoutcert-live-au.adyen.com\n\n\nLive\nUS\ncheckoutcert-live-us.adyen.com\n\n\n\nIn the live environment, the Checkout mutual authentication endpoint is regional. Do not use the {PREFIX}-checkout-live.adyenpayments.com endpoint that you use for API key authentication.\nFor Payment, Recurring, Payout, and BinLookup APIs:\n\n\n\nEnvironment\nRegion\nMutual authentication endpoint\n\n\n\n\nTest\nAll\npalcert-test.adyen.com\n\n\nLive\nAll\npalcert-live.adyen.com\n\n\n\nFor example, to send a  \/paymentMethods request with mutual authentication in the EU region, use https:\/\/checkoutcert-live-eu.adyen.com\/v72\/paymentMethods.\nMake a request with your client certificate\n\n\n    \n        \n        \n    \n\n\nUse a certificate from another certificate authority\nIf you must use a certificate that is signed by another certificate authority, Adyen requires that:\n\nA certificate authority that Adyen trusts signs the certificate.\nThe subject DN of the certificate matches the subject DN stored for the API credential.\n\nAfter you get your certificate, link it to your API credential. If you are not sure whether Adyen trusts the certificate authority, contact our Support Team.\nSee also\n\n\n                    API credentials\n                \n                    API credential roles\n                \n                    Live endpoints\n                \n                    API authentication\n                \n","type":"page","locale":"en","boost":18,"hierarchy":{"lvl0":"Home","lvl1":"Development resources","lvl2":"Mutual authentication"},"hierarchy_url":{"lvl0":"https:\/\/docs.adyen.com\/","lvl1":"https:\/\/docs.adyen.com\/development-resources","lvl2":"\/development-resources\/mtls"},"levels":3,"category":"Development Resources","category_color":"green","tags":["Mutual","authentication"]}}
