> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://developer.americanexpress.ferndocs.com/fraud-prevention/v2/amex-token-service/api-reference/provision-a-four-digit-security-code-for-a-transaction/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.americanexpress.ferndocs.com/_mcp/server. # Provision a four-digit security code for a transaction. POST https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens Content-Type: application/json Returns a payment credential for a transaction context. Reference: https://developer.americanexpress.ferndocs.com/fraud-prevention/amex-token-service/api-reference/provision-a-four-digit-security-code-for-a-transaction ## Authentication - `Authorization` header (required) — HMAC (one-way TLS). A `MAC` authorization header signed with your client secret. See https://developer.americanexpress.com/documentation/api-security/hmac ## Servers - `https://api.qa.americanexpress.com/payments/digital/v2/tokens` (Sandbox, default) - `https://api.americanexpress.com/payments/digital/v2/tokens` (Production) ## Request ### Headers - `Accept-Language` (string, required) — This is en-US. - `Content-Language` (string, required) — This is en-US. - `Authorization` (string, required) — The HMAC authorization header generated as prescribed by American Express API security.e.g., MAC id="OLQkWT14WtLR0aE63AqtkW2DJppMviSk", ts="1548353658039", nonce="18036bb8-a100-4e02-ab93-328abd67acf2", bodyhash="uRphuQfK6igW44z4Ns/Bo9XdiXlsCdEzTsxdeUBu9j8=", mac="yJ70ObsprC2ygCjzq88Lq0QTKPqlLMIPYpR4O1DBg+Y=". - `x-amex-api-key` (string, required) — The Client ID displayed on the American Express Token Service dashboard.e.g., OLQkWT14WtLR0aE63AqtkW2DJppMviSk - `x-amex-token-requester-id` (string, required) — The unique identifier as a Token Requester. This is available on the American Express Token Service dashboard.e.g., devportalTest - `x-amex-request-id` (string, required) — The unique identifier for the API request to be returned in the response headers.It is set by the API caller, and it should never be re-used across different transactions.e.g., AA3434342323 ### Body (application/json) This endpoint expects a purchasetokens_request. - `token_ref_id` (string, optional) — The unique reference identifier for a token. - `risk_assessment_data` (PurchasetokensRequestRiskAssessmentData, optional) — The User's risk profile on the Token Requester's domain. - `encrypted_payload` (PurchasetokensRequestEncryptedPayload, optional) — The base64-encoded JWE(Json Web Encryption) string containing the PAN information. The JWE string should adhere to RFC 7516.Steps for encryption:The JWE encryption utility will generate an AES 128-bit dynamic data encryption key.The dynamic data encryption key should be wrapped with the static AES 256-bit key provided by American Express using the A256KW algorithm.Set the key identifier of the static AES key in the header.Use the JSON compact serialization.Set the resulting JWE blob in the encrypted_payload element.Key wrap algorithm: A256KWData encryption algorithm: AES/GCM/NoPadding ## Response ### 200 The request has been processed successfully.Upon a 200 response, the transaction was successfully processed and the token data and transaction data are returned in a response payload. - `encrypted_payload` (PurchasetokensSuccessResponseEncryptedPayload, optional) — The JSON Web-encrypted token and payment data for authorization. You will receive this as a base64-encoded string as per RFC 7516. The secure_token_data is encrypted using a dynamically-generated, 128-bit data encryption key using the A128GCM algorithm. The dynamic data encryption key is wrapped with the static AES 256-bit key provided by American Express, using the A256KW algorithm.Steps to decrypt:Base64 decodes the header and reads the key identifier. Looks up the static AES 256-bit key provided by American Express that has been mapped to the key identifier.Unwraps the dynamic data encryption key, using the static AES 256-bit key, by means of the A256KW algorithm.Decrypts the data using the 128-bit dynamic encryption key, by means of the A128GCM algorithm.Validates the authorization tag.The resulting JSON object conforms to the secure_token_data.Key wrap algorithm: A256KWData encryption algorithm: AES/GCM/NoPadding ## Errors ### 400 Bad Request Error The submitted request body is not valid. - `error_code` (string, optional) — An error code indicating the contents of the request body that triggered an error.e.g., 104000 - `error_type` (string, optional) — An error type specifying the type of error triggered for the given request body.e.g., invalid_json_error - `error_description` (string, optional) — An error description specifying the problem with the provided request body.e.g., request_body_not_parseable_json ### 401 Unauthorized Error The Authorization header sent with the given request failed internal validation checks. - `error_code` (string, optional) — An error code indicating the request was rejected due to an incorrect Authorization header value.e.g., 104010 - `error_type` (string, optional) — An error type specifying that the type of error triggered was related to the request's Authorization field using HMAC.e.g., invalid_hmac - `error_description` (string, optional) — An error description specifying that the received request's Authorization header does not align with the provided request body or has been generated incorrectly.e.g., invalid_hmac ### 429 Too Many Requests Error The rate at which the caller is allowed to call the service has been exceeded. The caller request rate should be reduced. - `error_code` (string, optional) — An error code indicating the request was rejected due to the frequency of requests to the API.e.g., 104290 - `error_type` (string, optional) — An error type indicating the request was rejected due to the rate of requests being sent by the Token Requester.e.g., rate_limit_violation - `error_description` (string, optional) — An error description indicating the request was rejected due to the rate at which requests are sent to the API exceeding the established request rate limit.e.g., rate_limit_exceeded ### 500 Internal Server Error Internal error. An error that has occurred within the American Express system. The request may be retried based on the aligned retry policy. - `error_code` (string, optional) — An error code indicating the request was rejected due to an error within the API.e.g., 105000 - `error_type` (string, optional) — An error type indicating the request was rejected due to a transient failure within the API.e.g., system_error - `error_description` (string, optional) — An error description indicating the request failed due to an internal issue with the API.e.g., internal_api_error ### 504 Gateway Timeout Error The API gateway has experienced a connection timeout. The request may be retried based on the aligned retry policy. - `error_code` (string, optional) — An error code indicating the request did not receive a response due to a timeout from the API.e.g., 105040 - `error_type` (string, optional) — An error type indicating the request failed to receive its response due to a timeout from the API.e.g., connection_timeout - `error_description` (string, optional) — An error description indicating the request failed to receive a response from the API due to a connection timeout.e.g., connection_timeout ## Types ### PurchasetokensRequestRiskAssessmentData The User's risk profile on the Token Requester's domain. - `account_input_method` (string, optional) — The mechanism by which the Card details were captured. The allowed values are:On File: If the Card details are on-file.User Input: If the User entered the Card details in the current session. - `pan_tenure_on_file` (string, optional) — The number of weeks since the User added the Card account details on file. - `ip_address` (string, optional) — The IPv6 or IPv4 address of the User's mobile device or browser. Required when the account_input_method is User Input and when token_ref_id is not present in the request.e.g.,IPv4: 111.111.111.1111IPv6: ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD:ABCD ### PurchasetokensRequestEncryptedPayload The base64-encoded JWE(Json Web Encryption) string containing the PAN information. The JWE string should adhere to RFC 7516.Steps for encryption:The JWE encryption utility will generate an AES 128-bit dynamic data encryption key.The dynamic data encryption key should be wrapped with the static AES 256-bit key provided by American Express using the A256KW algorithm.Set the key identifier of the static AES key in the header.Use the JSON compact serialization.Set the resulting JWE blob in the encrypted_payload element.Key wrap algorithm: A256KWData encryption algorithm: AES/GCM/NoPadding - `transaction_data` (PurchasetokensRequestEncryptedPayloadTransactionData, optional) — Contains the transaction details required for generating a payment credential. ### PurchasetokensSuccessResponseEncryptedPayload The JSON Web-encrypted token and payment data for authorization. You will receive this as a base64-encoded string as per RFC 7516. The secure_token_data is encrypted using a dynamically-generated, 128-bit data encryption key using the A128GCM algorithm. The dynamic data encryption key is wrapped with the static AES 256-bit key provided by American Express, using the A256KW algorithm.Steps to decrypt:Base64 decodes the header and reads the key identifier. Looks up the static AES 256-bit key provided by American Express that has been mapped to the key identifier.Unwraps the dynamic data encryption key, using the static AES 256-bit key, by means of the A256KW algorithm.Decrypts the data using the 128-bit dynamic encryption key, by means of the A128GCM algorithm.Validates the authorization tag.The resulting JSON object conforms to the secure_token_data.Key wrap algorithm: A256KWData encryption algorithm: AES/GCM/NoPadding - `secure_token_data` (PurchasetokensSuccessResponseEncryptedPayloadSecureTokenData, optional) — Includes the data to generate the payment credentials. ### PurchasetokensRequestEncryptedPayloadTransactionData Contains the transaction details required for generating a payment credential. - `amount` (integer, optional) — The transaction amount that has an accuracy of two decimal places, multiplied by 100.Required if available.e.g., Amount of 1234.56 is sent as 123456. - `merchant_id` (string, optional) — The 10-digit American Express SE number should be used in this field.e.g., 1234567890 - `payment_credential_type` (string, optional) — The credential format. The value should be set to "DCSC". ### PurchasetokensSuccessResponseEncryptedPayloadSecureTokenData Includes the data to generate the payment credentials. - `token_number` (string, optional) — The unique token number for the provided token_ref_id or Card Account Number provided in the request.e.g., 343434343434343 - `expiry_month` (integer, optional) — The token's expiration month as an integer.e.g., 3 - `expiry_year` (integer, optional) — The token's expiration year as a four-digit (YYYY) integer.e.g., 2022 - `payment_credential` (string, optional) — The payment credential corresponding to the completed transaction.e.g., 1234 - `payment_account_reference` (string, optional) — The PAR is a unique non-financial alphanumeric EMVCo data element used to identify payment accounts across multiple form factors, not intended to be a payment replacement or a Consumer identifier, to assist non-payment use cases that rely on PAN. Since PAR persists through PAN life cycle changes, PAR provides better longevity as an identifier than the PAN. ## Examples **Request** ```json {} ``` **Response** ```json { "encrypted_payload": { "secure_token_data": { "token_number": "string", "expiry_month": 1, "expiry_year": 1, "payment_credential": "string", "payment_account_reference": "string" } } } ``` **SDK Code** ```python import requests url = "https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens" payload = {} headers = { "Accept-Language": "Accept-Language", "Authorization": "", "Content-Language": "Content-Language", "x-amex-api-key": "x-amex-api-key", "x-amex-request-id": "x-amex-request-id", "x-amex-token-requester-id": "x-amex-token-requester-id", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens'; const options = { method: 'POST', headers: { 'Accept-Language': 'Accept-Language', Authorization: '', 'Content-Language': 'Content-Language', 'x-amex-api-key': 'x-amex-api-key', 'x-amex-request-id': 'x-amex-request-id', 'x-amex-token-requester-id': 'x-amex-token-requester-id', 'Content-Type': 'application/json' }, body: '{}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens" payload := strings.NewReader("{}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Accept-Language", "Accept-Language") req.Header.Add("Authorization", "") req.Header.Add("Content-Language", "Content-Language") req.Header.Add("x-amex-api-key", "x-amex-api-key") req.Header.Add("x-amex-request-id", "x-amex-request-id") req.Header.Add("x-amex-token-requester-id", "x-amex-token-requester-id") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Accept-Language"] = 'Accept-Language' request["Authorization"] = '' request["Content-Language"] = 'Content-Language' request["x-amex-api-key"] = 'x-amex-api-key' request["x-amex-request-id"] = 'x-amex-request-id' request["x-amex-token-requester-id"] = 'x-amex-token-requester-id' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens") .header("Accept-Language", "Accept-Language") .header("Authorization", "") .header("Content-Language", "Content-Language") .header("x-amex-api-key", "x-amex-api-key") .header("x-amex-request-id", "x-amex-request-id") .header("x-amex-token-requester-id", "x-amex-token-requester-id") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('POST', 'https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens', [ 'body' => '{}', 'headers' => [ 'Accept-Language' => 'Accept-Language', 'Authorization' => '', 'Content-Language' => 'Content-Language', 'Content-Type' => 'application/json', 'x-amex-api-key' => 'x-amex-api-key', 'x-amex-request-id' => 'x-amex-request-id', 'x-amex-token-requester-id' => 'x-amex-token-requester-id', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens"); var request = new RestRequest(Method.POST); request.AddHeader("Accept-Language", "Accept-Language"); request.AddHeader("Authorization", ""); request.AddHeader("Content-Language", "Content-Language"); request.AddHeader("x-amex-api-key", "x-amex-api-key"); request.AddHeader("x-amex-request-id", "x-amex-request-id"); request.AddHeader("x-amex-token-requester-id", "x-amex-token-requester-id"); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Accept-Language": "Accept-Language", "Authorization": "", "Content-Language": "Content-Language", "x-amex-api-key": "x-amex-api-key", "x-amex-request-id": "x-amex-request-id", "x-amex-token-requester-id": "x-amex-token-requester-id", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.qa.americanexpress.com/payments/digital/v2/tokens/purchasetokens")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```