Standalone exemption

Send a standalone server-to-server request to determine whether a transaction qualifies for an exemption.

📘

You must be fully PCI compliant to perform a standalone exemption request, as it requires collecting card data.

1. Send the standalone exemption request

Use the standalone endpoint to send requests to the exemption engine: https://sandbox-card.peachpayments.com/v1/exemption

curl https://sandbox-card.peachpayments.com/v1/exemption \
 -d "entityId=8a8294174e735d0c014e78cf26461790" \
 -d "amount=12.50" \
 -d "currency=EUR" \
 -d "paymentBrand=VISA" \
 -d "card.number=4200000000000042" \
 -d "card.holder=John Smith" \
 -d "card.expiryMonth=12" \
 -d "card.expiryYear=2026" \
 -d "card.cvv=123" \
 -d "risk.parameters[EX_merchant.mcc]=3000" \
 -d "risk.parameters[EX_merchant.postcode]=0125EH" \
 -d "risk.parameters[EX_channelId]=000503000001" \
 -d "risk.parameters[EX_serviceId]=I" \
 -d "[email protected]" \
 -d "testMode=EXTERNAL" \
 -H "Authorization: Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ="
public Dictionary<string, dynamic> Request() {
    Dictionary<string, dynamic> responseData;
    string data="entityId=8a8294174e735d0c014e78cf26461790" +
        "&amount=12.50" +
        "&currency=EUR" +
        "&paymentBrand=VISA" +
        "&card.number=4200000000000042" +
        "&card.holder=John Smith" +
        "&card.expiryMonth=12" +
        "&card.expiryYear=2026" +
        "&card.cvv=123" +
        "&risk.parameters[EX_merchant.mcc]=3000" +
        "&risk.parameters[EX_merchant.postcode]=0125EH" +
        "&risk.parameters[EX_channelId]=000503000001" +
        "&risk.parameters[EX_serviceId]=I" +
        "&[email protected]" +
        "&testMode=EXTERNAL";
    string url = "https://sandbox-card.peachpayments.com/v1/exemption";
    byte[] buffer = Encoding.ASCII.GetBytes(data);
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
    request.Method = "POST";
    request.Headers["Authorization"] = "Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=";
    request.ContentType = "application/x-www-form-urlencoded";
    Stream PostData = request.GetRequestStream();
    PostData.Write(buffer, 0, buffer.Length);
    PostData.Close();
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    {
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        var s = new JavaScriptSerializer();
        responseData = s.Deserialize<Dictionary<string, dynamic>>(reader.ReadToEnd());
        reader.Close();
        dataStream.Close();
    }
    return responseData;
}

responseData = Request()["result"]["description"];
import groovy.json.JsonSlurper

public static String request() {
    def data = "entityId=8a8294174e735d0c014e78cf26461790" +
        "&amount=12.50" +
        "&currency=EUR" +
        "&paymentBrand=VISA" +
        "&card.number=4200000000000042" +
        "&card.holder=John Smith" +
        "&card.expiryMonth=12" +
        "&card.expiryYear=2026" +
        "&card.cvv=123" +
        "&risk.parameters[EX_merchant.mcc]=3000" +
        "&risk.parameters[EX_merchant.postcode]=0125EH" +
        "&risk.parameters[EX_channelId]=000503000001" +
        "&risk.parameters[EX_serviceId]=I" +
        "&[email protected]" +
        "&testMode=EXTERNAL"
    
    def url = "https://sandbox-card.peachpayments.com/v1/exemption".toURL()
    def connection = url.openConnection()
    connection.setRequestMethod("POST")
    connection.setRequestProperty("Authorization", "Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=")
    connection.doOutput = true
    connection.outputStream << data
    def json = new JsonSlurper().parseText(connection.inputStream.text)
    
    json
}

println request()
private String request() throws IOException {
    URL url = new URL("https://sandbox-card.peachpayments.com/v1/exemption");

    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    conn.setRequestProperty("Authorization", "Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=");
    conn.setDoInput(true);
    conn.setDoOutput(true);

    String data = ""
        + "entityId=8a8294174e735d0c014e78cf26461790"
        + "&amount=12.50"
        + "&currency=EUR"
        + "&paymentBrand=VISA"
        + "&card.number=4200000000000042"
        + "&card.holder=John Smith"
        + "&card.expiryMonth=12"
        + "&card.expiryYear=2026"
        + "&card.cvv=123"
        + "&risk.parameters[EX_merchant.mcc]=3000"
        + "&risk.parameters[EX_merchant.postcode]=0125EH"
        + "&risk.parameters[EX_channelId]=000503000001"
        + "&risk.parameters[EX_serviceId]=I"
        + "&[email protected]"
        + "&testMode=EXTERNAL";

    DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
    wr.writeBytes(data);
    wr.flush();
    wr.close();

    int responseCode = conn.getResponseCode();
    InputStream is;

    if (responseCode >= 400) {
        is = conn.getErrorStream();
    } else {
        is = conn.getInputStream();
    }

    return IOUtils.toString(is);
}
const https = require('https');
const querystring = require('querystring');

const request = async () => {
    const path = '/v1/exemption';
    const data = querystring.stringify({
        'entityId': '8a8294174e735d0c014e78cf26461790',
        'amount': '12.50',
        'currency': 'EUR',
        'paymentBrand': 'VISA',
        'card.number': '4200000000000042',
        'card.holder': 'John Smith',
        'card.expiryMonth': '12',
        'card.expiryYear': '2026',
        'card.cvv': '123',
        'risk.parameters[EX_merchant.mcc]': '3000',
        'risk.parameters[EX_merchant.postcode]': '0125EH',
        'risk.parameters[EX_channelId]': '000503000001',
        'risk.parameters[EX_serviceId]': 'I',
        'customer.email': '[email protected]',
        'testMode': 'EXTERNAL'
    });

    const options = {
        port: 443,
        host: 'sandbox-card.peachpayments.com',
        path: path,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': data.length,
            'Authorization': 'Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ='
        }
    };

    return new Promise((resolve, reject) => {
        const postRequest = https.request(options, function (res) {
            const buf = [];
            res.on('data', chunk => {
                buf.push(Buffer.from(chunk));
            });
            res.on('end', () => {
                const jsonString = Buffer.concat(buf).toString('utf8');
                try {
                    resolve(JSON.parse(jsonString));
                } catch (error) {
                    reject(error);
                }
            });
        });

        postRequest.on('error', reject);
        postRequest.write(data);
        postRequest.end();
    });
};

request().then(console.log).catch(console.error);
function request() {
    $url = "https://sandbox-card.peachpayments.com/v1/exemption";
    $data = "entityId=8a8294174e735d0c014e78cf26461790" .
            "&amount=12.50" .
            "&currency=EUR" .
            "&paymentBrand=VISA" .
            "&card.number=4200000000000042" .
            "&card.holder=John Smith" .
            "&card.expiryMonth=12" .
            "&card.expiryYear=2026" .
            "&card.cvv=123" .
            "&risk.parameters[EX_merchant.mcc]=3000" .
            "&risk.parameters[EX_merchant.postcode]=0125EH" .
            "&risk.parameters[EX_channelId]=000503000001" .
            "&risk.parameters[EX_serviceId]=I" .
            "&[email protected]" .
            "&testMode=EXTERNAL";

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization:Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ='
    ));
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // this should be set to true in production
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    $responseData = curl_exec($ch);
    
    if (curl_errno($ch)) {
        return curl_error($ch);
    }
    
    curl_close($ch);
    return $responseData;
}

$responseData = request();
try:
    from urllib.parse import urlencode
    from urllib.request import build_opener, Request, HTTPHandler
    from urllib.error import HTTPError, URLError
except ImportError:
    from urllib import urlencode
    from urllib2 import build_opener, Request, HTTPHandler, HTTPError, URLError
import json

def request():
    url = "https://sandbox-card.peachpayments.com/v1/exemption"
    data = {
        'entityId': '8a8294174e735d0c014e78cf26461790',
        'amount': '12.50',
        'currency': 'EUR',
        'paymentBrand': 'VISA',
        'card.number': '4200000000000042',
        'card.holder': 'John Smith',
        'card.expiryMonth': '12',
        'card.expiryYear': '2026',
        'card.cvv': '123',
        'risk.parameters[EX_merchant.mcc]': '3000',
        'risk.parameters[EX_merchant.postcode]': '0125EH',
        'risk.parameters[EX_channelId]': '000503000001',
        'risk.parameters[EX_serviceId]': 'I',
        'customer.email': '[email protected]',
        'testMode': 'EXTERNAL'
    }
    try:
        opener = build_opener(HTTPHandler)
        request = Request(url, data=urlencode(data).encode('utf-8'))
        request.add_header('Authorization', 'Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=')
        request.get_method = lambda: 'POST'
        response = opener.open(request)
        return json.loads(response.read())
    except HTTPError as e:
        return json.loads(e.read())
    except URLError as e:
        return e.reason

responseData = request()
print(responseData)
require 'net/https'
require 'uri'
require 'json'

def request()
  uri = URI('https://sandbox-card.peachpayments.com/v1/exemption')
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  req = Net::HTTP::Post.new(uri.path)
  req.set_form_data({
    'entityId' => '8a8294174e735d0c014e78cf26461790',
    'amount' => '12.50',
    'currency' => 'EUR',
    'paymentBrand' => 'VISA',
    'card.number' => '4200000000000042',
    'card.holder' => 'John Smith',
    'card.expiryMonth' => '12',
    'card.expiryYear' => '2026',
    'card.cvv' => '123',
    'risk.parameters[EX_merchant.mcc]' => '3000',
    'risk.parameters[EX_merchant.postcode]' => '0125EH',
    'risk.parameters[EX_channelId]' => '000503000001',
    'risk.parameters[EX_serviceId]' => 'I',
    'customer.email' => '[email protected]',
    'testMode' => 'EXTERNAL'
  })
  res = http.request(req)
  return JSON.parse(res.body)
end

puts request()
def initialPayment: String = {
  val url = "https://sandbox-card.peachpayments.com/v1/exemption"
  val data = ("" +
    "entityId=8a8294174e735d0c014e78cf26461790" +
    "&amount=12.50" +
    "&currency=EUR" +
    "&paymentBrand=VISA" +
    "&card.number=4200000000000042" +
    "&card.holder=John Smith" +
    "&card.expiryMonth=12" +
    "&card.expiryYear=2026" +
    "&card.cvv=123" +
    "&risk.parameters[EX_merchant.mcc]=3000" +
    "&risk.parameters[EX_merchant.postcode]=0125EH" +
    "&risk.parameters[EX_channelId]=000503000001" +
    "&risk.parameters[EX_serviceId]=I" +
    "&[email protected]" +
    "&testMode=EXTERNAL"
  )
  val conn = new URL(url).openConnection()

  conn match {
    case secureConn: HttpsURLConnection => secureConn.setRequestMethod("POST")
    case _ => throw new ClassCastException
  }
  conn.setDoInput(true)
  conn.setDoOutput(true)
  IOUtils.write(data, conn.getOutputStream())
  conn.setRequestProperty("Authorization", "Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=")
  conn.connect()
  
  if (conn.getResponseCode() >= 400) {
    return IOUtils.toString(conn.getErrorStream())
  } else {
    return IOUtils.toString(conn.getInputStream())
  }
}
Public Function Request() As Dictionary(Of String, Object)
    Dim url As String = "https://sandbox-card.peachpayments.com/v1/exemption"
    Dim data As String = "" +
        "entityId=8a8294174e735d0c014e78cf26461790" +
        "&amount=12.50" +
        "&currency=EUR" +
        "&paymentBrand=VISA" +
        "&card.number=4200000000000042" +
        "&card.holder=John Smith" +
        "&card.expiryMonth=12" +
        "&card.expiryYear=2026" +
        "&card.cvv=123" +
        "&risk.parameters[EX_merchant.mcc]=3000" +
        "&risk.parameters[EX_merchant.postcode]=0125EH" +
        "&risk.parameters[EX_channelId]=000503000001" +
        "&risk.parameters[EX_serviceId]=I" +
        "&[email protected]" +
        "&testMode=EXTERNAL"

    Dim req As WebRequest = WebRequest.Create(url)
    req.Method = "POST"
    req.Headers.Add("Authorization", "Bearer OGE4Mjk0MTc0ZTczNWQwYzAxNGU3OGNmMjY2YjE3OTR8SFV3I3JGQTQ9bWpxaWYrPz9OWVQ=")
    req.ContentType = "application/x-www-form-urlencoded"
    Dim byteArray As Byte() = Encoding.UTF8.GetBytes(data)
    req.ContentLength = byteArray.Length
    Dim dataStream As Stream = req.GetRequestStream()
    dataStream.Write(byteArray, 0, byteArray.Length)
    dataStream.Close()
    Dim res As WebResponse = req.GetResponse()
    Dim resStream = res.GetResponseStream()
    Dim reader As New StreamReader(resStream)
    Dim response As String = reader.ReadToEnd()
    reader.Close()
    resStream.Close()
    res.Close()
    Dim jss As New System.Web.Script.Serialization.JavaScriptSerializer()
    Dim dict As Dictionary(Of String, Object) = jss.Deserialize(Of Dictionary(Of String, Object))(response)

    Return dict
End Function

responseData = Request()("result")("description")
{
  "id":"8ac7a4a19d29e143019d29e9df7a1835",
  "paymentBrand":"VISA",
  "amount":"12.50",
  "currency":"EUR",
  "result":{
    "code":"000.300.103",
    "description":"Exemption check successful"
  },
  "resultDetails":{
    "RiskFraudTSWHitFlag":"N",
    "IovationAccountCode":"[email protected]",
    "RiskFraudTREHitFlag":"Y",
    "EXTERNAL_SYSTEM_LINK":"https://csi-stage.aciondemand.com/mars/en-US/search/transaction/000503000001ACQ20260326073126518",
    "RiskFraudCNBNumHits":"0",
    "RiskReport":"0330",
    "RiskRuleCategory":"SCAEX_01",
    "RiskFraudMaxTof":"1757",
    "TransactionId":"3839.0943.1332",
    "RiskFraudMinTof":"1",
    "RiskFraudCNBHitFlag":"N",
    "RiskFraudSDSNumHits":"0",
    "RiskFraudVirtualBin":"840",
    "RiskFraudTRENumHits":"1",
    "action":"created",
    "RiskOrderId":"000503000001ACQ20260326073126518",
    "RiskFraudSDSHitFlag":"N",
    "RiskFraudCardClass":"VIS",
    "RiskFraudTSWNumHits":"0"
  },
  "card":{
    "bin":"420000",
    "last4Digits":"0042",
    "holder":"John Smith",
    "expiryMonth":"12",
    "expiryYear":"2027"
  },
  "customer":{
    "email":"[email protected]"
  },
  "risk":{
    "parameters":{
      "EX_channelId":"000503000001",
      "EX_merchant.mcc":"3000",
      "EX_merchant.postcode":"0125EH",
      "EX_serviceId":"I"
    }
  },
  "buildNumber":"9092e7a6af8301accda2f9a3a38f743f907dadd5@2026-03-23 16:50:06 +0000",
  "timestamp":"2026-03-26 11:31:26+0000",
  "ndc":"8a8294174e735d0c014e78cf26461790_8a8c4451de474e93a1be6849f8aa87e8"
}

2. Handle the response

To determine which exemption applies, check the value in the resultDetails.RiskRuleCategory field. This value is always prefixed with SCAEX_. The two-digit value after the prefix indicates the applicable exemption:

  • 00: Exemption is not applicable
  • 01: Low-value exemption is applicable
  • 02: TRA exemption is applicable
  • 03: Trusted beneficiary exemption is applicable
  • 04: Corporate card exemption is applicable

To use an exemption during payment, include threeDSecure.exemptionFlag in the payment request with the applicable two-digit value.