Encrypt sensitive fields in your payment requests
Last updated: October 21, 2026
With field-level encryption, you encrypt sensitive values in your request body with a public key that Checkout.com issues to you. You then send the encrypted values to our API. Only Checkout.com holds the matching private key, so the values are not readable in transit or in any system between you and us. This includes your own logs, proxies, and load balancers.
You can encrypt individual fields, like the card number, or the entire request body.
Information
To enable field-level encryption, contact your account manager or request support.
- You generate an RSA key pair with Checkout.com. You keep the public key and its key ID (
kid). We store the private key securely on our side. - You encrypt the values you want to protect as JWE (JSON Web Encryption) tokens, using the compact serialization format and the public key.
- You send the request with the encrypted values instead of the plain values. In the
Cko-Encryption-Key-Idrequest header, declare thekidyou used, and in theCko-Encrypted-Propertiesheader, declare the fields you encrypted. - We match your
kidto the private key, decrypt the values, and process the request as if you had not encrypted it. - Your response is unchanged. Field-level encryption applies to requests only.
You can encrypt the entire request body, or encrypt specific fields within it.
| What you encrypt | What it covers | Headers to send |
|---|---|---|
The entire request body | Every field in the request. The body you send is the JWE token itself, not JSON. |
|
| The entire source object, including the card number, expiry date, name, and billing address. |
|
| The card number only. |
|
| The entire destination object in a payout request, including the card number, expiry date, and account holder details. |
|
| The destination card number in a payout request only. |
|
We reject any request that encrypts an unsupported field with a 422 response and the invalid_encryption_property error code.
You can encrypt at most two fields per request. We reject a request that declares more with the encrypted_field_limit_exceeded error code. Encrypting the entire request body counts as one operation, so the limit does not apply to it.
Before encrypting any fields, make sure you have:
- Field-level encryption enabled for your client ID
- Your public key in PEM (Privacy-Enhanced Mail) format, and the key ID Checkout.com issued with it
- A JWE library that supports RSA-OAEP-256 key encryption and A256GCM content encryption
Warning
Never send us your private key, and never use a key pair you generated yourself. If your key pair is compromised, contact your account manager or request support to get a replacement.
Every token you send must carry a protected header with the following values. Checkout.com rejects any other algorithm.
| Header | Required value |
|---|---|
| RSA-OAEP-256 |
| A256GCM |
| The key ID we issued to you. It must match the |
To send an encrypted request, encrypt each value, and then add the encrypted values and the encryption headers to your request.
Convert the value to JSON first, and then encrypt that JSON. A string value keeps its surrounding quotes, so the value you encrypt for a card number is "4242424242424242", not 4242424242424242. An object value is encrypted as its JSON representation, for example {"type":"card","number":"4242424242424242","expiry_month":12,"expiry_year":2030}.
Encrypt each field separately. Each field becomes its own JWE token.
The following examples use JavaScript, Java, and Python, but any library that supports RSA-OAEP-256 and A256GCM works. In each example, replace the placeholder values with your public key and kid.
1import { readFileSync } from 'node:fs';2import { CompactEncrypt, importSPKI } from 'jose';34const CKO_PUBLIC_KEY_PEM = readFileSync('cko-public-key.pem', 'utf8');5const CKO_KEY_ID = 'key_01234';67async function encryptField(value) {8const publicKey = await importSPKI(CKO_PUBLIC_KEY_PEM, 'RSA-OAEP-256');910return new CompactEncrypt(new TextEncoder().encode(JSON.stringify(value)))11.setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM', kid: CKO_KEY_ID })12.encrypt(publicKey);13}1415encryptField('4242424242424242').then((encryptedNumber) => {16console.log(encryptedNumber);17});
The result is a compact JWE token, made up of five base64url segments separated by dots. For example:
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5XzAxMjM0In0.Xh8...Q.9dK...A.1sP...w.Zt4...g
Call the Request a payment or payout endpoint.
Information
Your base URL's {prefix} value is unique to your account and environment. To learn how to retrieve your base URLs for the sandbox and production environments, see API endpoints.
post
https://{prefix}.api.checkout.com/payments
Replace each plain value with its JWE token, and add the following headers:
Cko-Encryption-Key-Id– The key ID (kid) of the key you encrypted with. This header is required.Cko-Encrypted-Properties– A comma-separated list of the JSON paths you encrypted, for examplesource.number. Omit it only when you encrypt the entire request body.
To encrypt only the card number, send the following request, with the Cko-Encrypted-Properties: source.number header:
1{2"source": {3"type": "card",4"number": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5XzAxMjM0In0.Xh8...Q.9dK...A.1sP...w.Zt4...g",5"expiry_month": 12,6"expiry_year": 20307},8"amount": 1000,9"currency": "GBP"10}
To encrypt the entire source object, set Cko-Encrypted-Properties to source and send the following request body:
1{2"source": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoia2V5XzAxMjM0In0.Xh8...Q.9dK...A.1sP...w.Zt4...g",3"amount": 1000,4"currency": "GBP"5}
To encrypt the destination in a payout request, follow the same pattern. Set Cko-Encrypted-Properties to destination to encrypt the entire destination object, or to destination.number to encrypt the card number only.
If the request is valid, you receive the same response as for the equivalent request without encryption.
The following rules apply to encrypted requests:
- You can adopt encryption one field at a time. If a path you declare in
Cko-Encrypted-Propertiesholds a plain value, Checkout.com passes it through untouched. This lets you roll out encryption gradually. We ignore duplicate paths in the header. - Always send the
Cko-Encryption-Key-Idheader. If you send an encrypted body without it, or before your client is enabled, we do not decrypt the body. The JWE token reaches validation as plain text, and the request fails with a standard validation error. - Use one key ID per request. You must encrypt all tokens in a request with the key named in
Cko-Encryption-Key-Id. - Field-level encryption applies to requests only. We never encrypt responses, webhooks, or events with your key.
Failures return the standard validation error shape, with the specific reason in error_codes.
1{2"request_id": "0HN7ROOFDVJHQ:00000001",3"error_type": "request_invalid",4"error_codes": ["invalid_encryption_key"]5}
| Status | Error code | Cause | How to fix |
|---|---|---|---|
|
| The key ID is unknown, belongs to another account, or is not available in the region that serves your request. Alternatively, the | Send the key ID that Checkout.com issued to you. Make sure every token declares the same key ID in its protected header. |
|
| The token is not a well-formed JWE for the declared algorithms. For example, the encrypted key is unreadable, the IV (initialization vector) or authentication tag has the wrong length, or the encrypted data failed authentication. | Encrypt the value again. Do not change or re-encode the token after your library produces it. |
|
| We could not decrypt the token with the key mapped to your key ID. | Confirm that you encrypted with the current public key for that key ID, and then retry. If the error persists, contact your account manager. |
|
| The request declares more than two fields in | Declare at most two paths, or encrypt the entire source or destination object instead. |
|
| A declared path is not a supported field. | Declare only |
|
| The body is neither JSON nor a valid JWE, the JWE declares an unsupported | Check that the protected header declares RSA-OAEP-256 and A256GCM, and that the value you encrypted is valid JSON. |
To test your integration in the sandbox:
- Use your sandbox key ID and public key to send a payment with
source.numberencrypted. For card numbers to use, see Test cards. - Confirm that you receive a
201response with"approved": trueand the expected card details. This confirms that Checkout.com decrypted your token. - Change one character of the token and send the request again. You receive a
422response with theinvalid_encryption_payloaderror code. - Send the same request with a key ID that Checkout.com did not issue to you. You receive a
422response with theinvalid_encryption_keyerror code.
After your sandbox tests pass and your client ID is enabled in production, roll out encryption to your production traffic.
To rotate keys, request a new key pair, start encrypting with the new key ID, and retire the old key after all your traffic has moved to the new one.