--- title: "Mutual authentication" description: "Use a client certificate to verify the identity of the client when you send API requests to Adyen." url: "https://docs.adyen.com/development-resources/mtls" source_url: "https://docs.adyen.com/development-resources/mtls.md" canonical: "https://docs.adyen.com/development-resources/mtls" last_modified: "2026-07-30T12:00:00+02:00" language: "en" --- # Mutual authentication Use a client certificate to verify the identity of the client when you send API requests to Adyen. Mutual authentication, also known as two-way authentication, uses a [client certificate](https://en.wikipedia.org/wiki/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. Adyen acts as a [certificate authority](https://en.wikipedia.org/wiki/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. 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 [Use a certificate from another certificate authority](#external-ca). ## Requirements Before you begin, take into account the following requirements, limitations, and preparations. | Requirement | Description | | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[API credential roles](/development-resources/api-credentials/roles/)** | Make sure that your API credential has the following [roles](/development-resources/api-credentials/roles/#management-api):- **Management API - Client certificates read** to get your certificates. - **Management API - Client certificates read and write** to generate a certificate. | | **Setup steps** | Before you begin:- [Generate an API key](/development-resources/api-credentials/#generate-api-key). - Install [OpenSSL](https://www.openssl.org/) or another tool that can create a certificate signing request. - Install [jq](https://jqlang.github.io/jq/), or another tool that can parse JSON, to read the enrollment response. | | **Limitations** | * You 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](#supported-apis). | ## How it works To set up mutual authentication: 1. You create a private key and a certificate signing request. 2. You send the certificate signing request to Adyen. 3. Adyen signs the request and returns your client certificate and certificate chain. 4. You link the subject DN of the certificate to an API credential. 5. You send API requests to a [mutual authentication endpoint](#mutual-authentication-endpoints) with your certificate. Adyen accepts the request when the subject DN of the certificate matches the subject DN stored for the API credential. ## Generate a client certificate To generate a client certificate: 1. Create a private key and a certificate signing request: ```bash openssl req -new \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout client.key \ -out client.csr \ -subj "/CN=adyen-mtls-client" ``` OpenSSL 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. Adyen 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: ```text UID={apiCredential}-Company.{companyId},CN={apiCredential}-Company.{companyId},O=Company.{companyId} ``` Use any subject in the `-subj` value. 2. Convert the certificate signing request to a Base64-encoded DER string: ```bash openssl req -in client.csr -outform DER | openssl base64 -A ``` 3. Make a POST `/companies/{companyId}/clientCertificates` request. In the request, include: | Parameter | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `apiCredential` | The 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. | | `certificateSigningRequest` | The certificate signing request from the previous step, as a Base64-encoded DER string. | ```curl curl https://management-live.adyen.com/v3/companies/YOUR_COMPANY_ACCOUNT/clientCertificates \ -X POST \ -H 'x-API-key: ADYEN_API_KEY' \ -H 'content-type: application/json' \ -d '{ "apiCredential": "ws_123456@Company.YourCompanyAccount", "certificateSigningRequest": "YOUR_BASE64_ENCODED_CSR" }' ``` To 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](#mutual-authentication-endpoints). 4. Save the response to a file, for example `enrollment.json`. Note the following: | Parameter | Description | | ----------- | ------------------------------------------------------------------------------------------------------------ | | `leaf` | Your signed client certificate, as a Base64-encoded DER string. | | `caChain` | The certificate chain, including intermediate and root certificates. This does not include your certificate. | | `subjectDN` | The subject DN of your certificate. You need this in the next step. | 5. Assemble 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. From 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: ```bash jq -r '.leaf, .caChain[]' enrollment.json \ | while read -r cert; do \ printf '%s' "$cert" | openssl base64 -d -A | openssl x509 -inform DER; \ done > client.crt ``` Use `client.crt`, together with your private key, as the client certificate on every [mTLS request](#make-request-with-mtls). Optionally, verify the bundle: ```bash openssl crl2pkcs7 -nocrl -certfile client.crt | openssl pkcs7 -print_certs -noout ``` The output shows the `leaf` first, followed by each CA in the chain up to the root. Send 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. To 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. ## Link the certificate to your API credential 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. ### Tab: Customer Area 1. Log in to your [Customer Area](https://ca-live.adyen.com/). 2. Go to **Developers** > **API credentials**, and select the API credential that you used in the `apiCredential` field. 3. Under **Authentication**, in the **Client Certificate (DN)** field, enter the `subjectDN` from the response. 4. Select **Save**. ### Tab: Management API * Get the ID of your API credential. Make a GET [/companies/{companyId}/apiCredentials](https://docs.adyen.com/api-explorer/Management/latest/get/companies/\(companyId\)/apiCredentials) request, then find the credential whose `username` matches the `apiCredential` that you enrolled, and copy its `id`. The enroll request identifies your credential by username, but this request identifies it by ID. These are different values for the same credential. * Make a PATCH [/companies/{companyId}/apiCredentials/{apiCredentialId}](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/apiCredentials/\(apiCredentialId\)) request, and set [subjectDN](https://docs.adyen.com/api-explorer/Management/latest/patch/companies/\(companyId\)/apiCredentials/\(apiCredentialId\)#request-subjectDN) to the `subjectDN` from the response. ```curl curl https://management-live.adyen.com/v3/companies/YOUR_COMPANY_ACCOUNT/apiCredentials/YOUR_API_CREDENTIAL_ID \ -X PATCH \ -H 'x-API-key: ADYEN_API_KEY' \ -H 'content-type: application/json' \ -d '{ "subjectDN": "SUBJECT_DN_FROM_RESPONSE" }' ``` 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. ## Test your setup Before you link a certificate to a live API credential, test the same process in the test environment. To test your setup: 1. Generate a certificate with `https://management-test.adyen.com`. 2. Link the certificate to a test company-level API credential. 3. Send a request to a test mutual authentication endpoint with your certificate and private key: ```curl curl https://checkoutcert-test.adyen.com/v72/paymentMethods \ -X POST \ --cert client.crt \ --key client.key \ -H 'x-API-key: ADYEN_API_KEY' \ -H 'content-type: application/json' \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "countryCode": "NL", "amount": { "currency": "EUR", "value": 1000 } }' ``` Because `client.key` is encrypted, curl prompts for the passphrase. To send the request without a prompt, add `--pass YOUR_KEY_PASSPHRASE`. 4. Check that the request returns an HTTP response from Adyen. If the TLS handshake fails before you get an HTTP response, check that `client.crt` contains your certificate first, followed by the full chain. ## Supported APIs After you link a certificate, you must send requests to the following APIs over a mutual authentication connection with your client certificate: * [Checkout](https://docs.adyen.com/api-explorer/Checkout/latest/overview) * [Payment](https://docs.adyen.com/api-explorer/Payment/latest/overview) (classic integration) * [Recurring](https://docs.adyen.com/api-explorer/Recurring/latest/overview) * [Payout](https://docs.adyen.com/api-explorer/Payout/latest/overview) * [BinLookup](https://docs.adyen.com/api-explorer/BinLookup/latest/overview) 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. ## Endpoints for mutual authentication When you send an API request, use the endpoint that supports mutual authentication. For Checkout API: | Environment | Region | Mutual authentication endpoint | | ----------- | ------ | ---------------------------------- | | Test | All | **checkoutcert-test.adyen.com** | | Live | EU | **checkoutcert-live-eu.adyen.com** | | Live | AU | **checkoutcert-live-au.adyen.com** | | Live | US | **checkoutcert-live-us.adyen.com** | In 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. For Payment, Recurring, Payout, and BinLookup APIs: | Environment | Region | Mutual authentication endpoint | | ----------- | ------ | ------------------------------ | | Test | All | **palcert-test.adyen.com** | | Live | All | **palcert-live.adyen.com** | For example, to send a [/paymentMethods](https://docs.adyen.com/api-explorer/Checkout/latest/post/paymentMethods) request with mutual authentication in the EU region, use `https://checkoutcert-live-eu.adyen.com/v72/paymentMethods`. ## Make a request with your client certificate ### Tab: curl Send an API request as you normally do. For example, [get the available payment methods](/online-payments/build-your-integration/advanced-flow?platform=Web\&integration=API%20only#get-available-payment-methods). Make sure that you: 1. Pass `cert` and `key`. The `cert` file must contain your certificate and the full chain. 2. Use the correct [endpoint that supports mutual authentication](#mutual-authentication-endpoints). ```curl curl https://checkoutcert-live-eu.adyen.com/v72/paymentMethods \ -X POST \ --cert client.crt \ --key client.key \ -H 'x-API-key: ADYEN_API_KEY' \ -H 'content-type: application/json' \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "countryCode": "NL", "amount": { "currency": "EUR", "value": 1000 } }' ``` Because `client.key` is encrypted, curl prompts for the passphrase. To send the request without a prompt, add `--pass YOUR_KEY_PASSPHRASE`. ### Tab: Java Our Java API library supports mutual authentication. You can find the [latest version on GitHub](https://github.com/Adyen/adyen-java-api-library). To use mutual authentication when you send an API request with our Java API library: 1. Follow the instructions to [install the Java API library](/development-resources/libraries), if you have not done so already. 2. Find the `Client` constructor line: ```java Client client = new Client(xApiKey, Environment.LIVE); ``` Replace it with the following code: ```java KeyStore trustStore = CertificateUtil.loadKeyStore("TRUST_STORE_PATH", "KEY_TYPE", "TRUST_STORE_PASSWORD"); KeyStore keyStore = CertificateUtil.loadKeyStore("CLIENT_STORE_PATH", "KEY_TYPE", "KEYSTORE_PASSWORD"); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, "KEYSTORE_PASSWORD".toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); String apiKey = "ADYEN_API_KEY"; Client client = new Client(sslContext, apiKey); ``` This code configures the library for the live environment and requires the following imports: * `com.adyen.util.CertificateUtil` * `java.security.KeyStore` * `javax.net.ssl.KeyManagerFactory` * `javax.net.ssl.SSLContext` * `javax.net.ssl.TrustManagerFactory`. 3. Replace `TRUST_STORE_PATH`, `CLIENT_STORE_PATH`, `TRUST_STORE_PASSWORD`, `KEYSTORE_PASSWORD`, `KEY_TYPE`, and `ADYEN_API_KEY` with your values. 4. Use the correct [endpoint that supports mutual authentication](#mutual-authentication-endpoints). ## Use a certificate from another certificate authority If you must use a certificate that is signed by another certificate authority, Adyen requires that: * A [certificate authority](https://en.wikipedia.org/wiki/Certificate_authority) that Adyen trusts signs the certificate. * The subject DN of the certificate matches the subject DN stored for the [API credential](/development-resources/api-credentials). After you get your certificate, [link it to your API credential](#link-the-certificate-to-your-api-credential). If you are not sure whether Adyen trusts the certificate authority, contact our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other). ## See also * [API credentials](/development-resources/api-credentials) * [API credential roles](/development-resources/api-credentials/roles) * [Live endpoints](/development-resources/live-endpoints) * [API authentication](/development-resources/api-authentication)