Restaurant POS Integration Guide

Complete integration reference for restaurant merchants using the NCR PMI REST API — covering dine-in, bar tab, tip adjustment, split tender, and gift card operations.

Restaurant payment flows are fundamentally different from retail POS. A guest may sit at a table for an hour before paying, split the bill across multiple seats, add a tip after the card has been authorized, or pay part with a gift card and the rest with a credit card. This guide covers every scenario your restaurant POS needs to handle, with working API request/response examples for each.

🍽️ Restaurant vs Retail — Key Differences

Retail POS: one transaction per item scan. Restaurant POS: session spans from order-entry to final payment, often 30–90 minutes later, with post-authorization adjustments (tip, incremental auth, split tender) happening in between.

Onboarding & prerequisites

Before writing integration code, NCR Voyix works with you to provision a lab environment using the Azure RLSG-CFR platform. This environment is configured end-to-end during the customer onboarding process and includes all credentials, device configuration, and host connectivity needed for end-to-end transaction testing.

Onboarding package

Your onboarding package is assembled based on your business profile. It covers payment host connectivity, PIN pad device configuration, platform support, and repository access.

Supported payment hosts

Your environment is provisioned against one or more of the following payment processors:

  • Chase
  • Corpay / Comdata
  • Worldpay
  • Wex
  • Voyix Pay
  • Additional hosts as applicable

Supported PIN pad devices

DeviceSupported connection
Ingenico Lane/8000TCP/IP, USB
Moby 5500TCP/IP, USB
Verifone M425 Neo / M400TCP/IP, USB

Platform support

CCL and PMIWebServer are distributed for the following operating systems:

  • Windows
  • Linux
  • Android

Product feature enablement

Features are enabled based on your business segment during onboarding:

  • Hospitality — restaurant, bar, and table-service scenarios
  • Retail — Grocery
  • Retail — Fuel
  • Commercial Fuel
  • Additional features as applicable to your use case

SDK build & JFrog access

Your onboarding package specifies the CCL build version for your deployment (for example, v26.6.1) along with JFrog Artifactory credentials for downloading SDK packages and build artifacts.

Repository access

SDK packages are hosted under npg-ccl-generic-releases/CCL/26.6/ on NCR's JFrog Artifactory. Your onboarding team provides the access URL and credentials.

Configuration details

In addition to environment setup, your onboarding package includes merchant configuration for the following components:

DataManager & ConMan

Your CCL configuration is provisioned with the identifiers required for every API call:

ParameterDescription
CompanyNumber Merchant organization identifier. Included in every request body under ConfigOptions.
StoreNumber Store location identifier. Scoped to a single physical site.
LaneNumber POS terminal / register number within the store.
MID (Merchant ID) Host-specific identifier assigned by the payment processor for your merchant account.
TID (Terminal ID) Terminal-level identifier assigned per lane by the payment host.

Store-level and lane-level configurations are managed in DataManager. ConMan handles configuration delivery to the CCL engine at Initialize and OpenLane time.

Lab vs production credentials

The MID and TID values provided in your lab onboarding package are for test use only. Contact your NCR account team to obtain production values before go-live.

Before you begin

Ensure the following are in place before writing integration code:

  • CCL 26.6 SDK installed and PMIWebServer running on https://localhost:8600
  • Merchant credentials: Company Number, Store Number, Lane Number, Client ID
  • Lane initialized and open: GetEngineStatus → Initialize (if needed) → OpenLane. See the Retail PMI guide for startup details.
  • TLS certificate: PMIWebServer uses a self-signed cert. In non-production, disable cert verification. In production, install the NCR-provided cert.
⚠️ Always call EndPaymentSession

Every BeginPaymentSession must be paired with EndPaymentSession — even on error. Leaving sessions open will block subsequent payments. Use try/finally or equivalent in your implementation.

Restaurant payment use cases

The following table maps every scenario to the PMI API calls required:

ScenarioPMI APIs involvedSection
Standard dine-in
Guest pays full check at end of meal
BeginPaymentSession → GetCardDetails → Purchase → EndPaymentSession Standard payment
Bar tab
Card stored on file; charged when guest closes out
BeginPaymentSession → OpenTab → … → BeginPaymentSession → CloseTab (+ optional IncrementalAuth) Bar tab
Tip adjustment
Tip added after authorization (server-entered)
BeginPaymentSession → Adjustment → EndPaymentSession Tip adjustment
Device tip prompt
Guest selects tip % on PIN pad before authorization
GetCardDetails with PromptTip: "YES" → Purchase with total + tip Device tip
Split tender
Multiple payments on one check (e.g., gift card + card)
Multiple sequential BeginPaymentSession → Purchase cycles, each for a partial amount Split tender
Gift card as tender
Use gift card balance to pay (fully or partially)
BeginPaymentSession → GetCardDetails (CardType: GiftCard) → Purchase (isGiftCard) → EndPaymentSession Gift card tender
Gift card management
Activate / Reload / Deactivate / CashOut / BalanceInquiry
BeginPaymentSession → Activate | Reload | Deactivate | CashOut | BalanceInquiry → EndPaymentSession Gift card mgmt

Payment flow diagrams

Standard dine-in payment

POS App Your system PMI WebServer localhost:8600 PIN Pad Verifone / Ingenico Payment Host NCR Voyix Gateway BeginPaymentSession ← SessionTranID: "ccl-session-001" StartNotifications GetCardDetails (Amount, SessionTranID) Activate PIN pad — prompt for card ← Tap / Insert / Swipe → paymentTranId ← CardBrand, LastFour, PaymentTranID Purchase (Amount, PaymentTranID) Authorize + capture at gateway ← AuthCode, ReferenceID, AmountApproved ← AuthCode, CardBrand ••••LastFour EndPaymentSession ✓ Save: AuthCode, ReferenceID, PaymentTranID — required for Adjustment

Bar tab — OpenTab → CloseTab

POS App Your system PMI WebServer localhost:8600 PIN Pad Card reader Payment Host NCR Voyix Gateway OPEN TAB BeginPaymentSession OpenTab (SessionTranID) Activate PIN pad — card tap/swipe Card stored at gateway — no charge yet ← referenceId, paymentTranId (save both!) EndPaymentSession ⏳ Guest continues ordering… (minutes to hours later) CLOSE TAB BeginPaymentSession (new session) CloseTab (referenceId, paymentTranId, Amount, Tip) Retrieve stored card + authorize final amount ← AuthCode, AmountApproved (includes tip) EndPaymentSession

Session lifecycle

Every PMI payment operation is wrapped in a session. The session ID (SessionTranID) returned by BeginPaymentSession must be included in all subsequent calls within that session.

Step 1
BeginPaymentSession

Open a session. Receive SessionTranID. Required before any payment operation.

Step 2
Payment Operation

GetCardDetails, Purchase, OpenTab, CloseTab, Adjustment, Activate, etc.

Step 3
EndPaymentSession

Always close the session. Required even on errors or cancellations.

Session rule

Every BeginPaymentSession must be followed by EndPaymentSession — even on error or cancellation. A session left open will block all further payment operations on that lane.

Standard dine-in payment

🍽️ Scenario: Full check payment at end of meal

The most common restaurant flow. The server presents the check, the guest taps/inserts their card, and the full amount is captured in one transaction.

Open a payment session

curl -X POST https://localhost:8600/PMI/BeginPaymentSession \
  -H "Content-Type: application/json" \
  -H "X-ClientId: POS1" \
  --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1",
      "MessageType": "Request",
      "Operation": "BeginPaymentSession",
      "RequestID": "1001",
      "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
    }
  }'

# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "SessionTranID": "ccl-session-001" } } }
// Node.js 18+ (built-in fetch). Set NODE_TLS_REJECT_UNAUTHORIZED=0 for self-signed cert.
const response = await fetch('https://localhost:8600/PMI/BeginPaymentSession', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({
    Message: {
      ClientID: 'POS1', MessageType: 'Request',
      Operation: 'BeginPaymentSession', RequestID: '1001', Version: '4.7.0',
      ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
    }
  })
});
const { Message } = await response.json();
const sessionTranID = Message.PaymentDetails.SessionTranID;
import requests

resp = requests.post(
    'https://localhost:8600/PMI/BeginPaymentSession',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={
        'Message': {
            'ClientID': 'POS1', 'MessageType': 'Request',
            'Operation': 'BeginPaymentSession', 'RequestID': '1001', 'Version': '4.7.0',
            'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}
        }
    },
    verify=False  # self-signed cert on localhost
)
msg = resp.json()['Message']
session_tran_id = msg['PaymentDetails']['SessionTranID']
 [
        'ClientID' => 'POS1',
        'MessageType' => 'Request',
        'Operation' => 'BeginPaymentSession',
        'RequestID' => '1001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/BeginPaymentSession');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$sessionTranID = $msg['PaymentDetails']['SessionTranID'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        MessageType = "Request",
        Operation = "BeginPaymentSession",
        RequestID = "1001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/BeginPaymentSession", payload);
var root = await resp.Content.ReadFromJsonAsync();
var sessionTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("SessionTranID").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/BeginPaymentSession')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    MessageType: 'Request',
    Operation: 'BeginPaymentSession',
    RequestID: '1001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
session_tran_id = msg['PaymentDetails']['SessionTranID']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","MessageType":"Request","Operation":"BeginPaymentSession","RequestID":"1001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/BeginPaymentSession"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.SessionTranID
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "MessageType": "Request",
        "Operation": "BeginPaymentSession",
        "RequestID": "1001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/BeginPaymentSession", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["SessionTranID"]}

Start notifications and get card details

Call StartNotifications first to activate the PIN pad display, then call GetCardDetails which waits for the customer to present their card.

curl -X POST https://localhost:8600/PMI/GetCardDetails \
  -H "Content-Type: application/json" \
  -H "X-ClientId: POS1" \
  --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "GetCardDetails",
      "RequestID": "1002", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
      "PaymentDetails": { "SessionTranID": "ccl-session-001" },
      "CardDetails": [{ "CardType": "Credit" }]
    }
  }'

# Response
# { "Message": { "StatusCode": 0,
#     "PaymentDetails": { "PaymentTranID": "pmt-tran-abc123" },
#     "CardDetails": [{ "CardBrand": "Visa", "LastFour": "4242", "EntryMethod": "Tap" }] } }
const response = await fetch('https://localhost:8600/PMI/GetCardDetails', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({
    Message: {
      ClientID: 'POS1', Operation: 'GetCardDetails',
      RequestID: '1002', Version: '4.7.0',
      ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
      Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
      PaymentDetails: { SessionTranID: sessionTranID },
      CardDetails: [{ CardType: 'Credit' }]
    }
  })
});
const { Message } = await response.json();
const paymentTranID = Message.PaymentDetails.PaymentTranID;
const { CardBrand, LastFour } = Message.CardDetails[0];
resp = requests.post(
    'https://localhost:8600/PMI/GetCardDetails',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={
        'Message': {
            'ClientID': 'POS1', 'Operation': 'GetCardDetails',
            'RequestID': '1002', 'Version': '4.7.0',
            'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
            'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
            'PaymentDetails': {'SessionTranID': session_tran_id},
            'CardDetails': [{'CardType': 'Credit'}]
        }
    }, verify=False
)
msg = resp.json()['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID']
 [
        'ClientID' => 'POS1',
        'Operation' => 'GetCardDetails',
        'RequestID' => '1002',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '52.75',
            'AmountTendered' => '52.75'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-001'
        ],
        'CardDetails' => [[
            'CardType' => 'Credit'
        ]]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$paymentTranID = $msg['PaymentDetails']['PaymentTranID'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "GetCardDetails",
        RequestID = "1002",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "52.75",
            AmountTendered = "52.75"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-001"
        },
        CardDetails = new[] {{'CardType': 'Credit'}}
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var paymentTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("PaymentTranID").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'GetCardDetails',
    RequestID: '1002',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '52.75',
      AmountTendered: '52.75'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-001'
    },
    CardDetails: [{'CardType': 'Credit'}]
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"1002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-001"},"CardDetails":[{"CardType":"Credit"}]}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.PaymentTranID
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "GetCardDetails",
        "RequestID": "1002",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "52.75",
            "AmountTendered": "52.75",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-001",
        },
        "CardDetails": []any{{'CardType': 'Credit'}},
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["PaymentTranID"]}

Authorize and capture — Purchase

curl -X POST https://localhost:8600/PMI/Purchase \
  -H "Content-Type: application/json" \
  -H "X-ClientId: POS1" \
  --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Purchase",
      "RequestID": "1003", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
      "PaymentDetails": {
        "SessionTranID": "ccl-session-001",
        "PaymentTranID": "pmt-tran-abc123"
      }
    }
  }'

# Response
# { "Message": { "StatusCode": 0,
#     "PaymentDetails": { "AuthCode": "AUTH123456", "ResponseMessage": "APPROVED" },
#     "HostData": { "ReferenceID": "ref-host-789xyz" },
#     "Amounts": { "AmountApproved": "52.75" } } }
const response = await fetch('https://localhost:8600/PMI/Purchase', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({
    Message: {
      ClientID: 'POS1', Operation: 'Purchase',
      RequestID: '1003', Version: '4.7.0',
      ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
      Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
      PaymentDetails: { SessionTranID: sessionTranID, PaymentTranID: paymentTranID }
    }
  })
});
const { Message } = await response.json();
// ⚠️ Save these — needed for Adjustment later
const authCode = Message.PaymentDetails.AuthCode;
const referenceID = Message.HostData.ReferenceID;
resp = requests.post(
    'https://localhost:8600/PMI/Purchase',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={
        'Message': {
            'ClientID': 'POS1', 'Operation': 'Purchase',
            'RequestID': '1003', 'Version': '4.7.0',
            'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
            'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
            'PaymentDetails': {'SessionTranID': session_tran_id, 'PaymentTranID': payment_tran_id}
        }
    }, verify=False
)
msg = resp.json()['Message']
auth_code = msg['PaymentDetails']['AuthCode']
reference_id = msg['HostData']['ReferenceID']  # Save for Adjustment
 [
        'ClientID' => 'POS1',
        'Operation' => 'Purchase',
        'RequestID' => '1003',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '52.75',
            'AmountTendered' => '52.75'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-001',
            'PaymentTranID' => 'pmt-tran-abc123'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/Purchase');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "Purchase",
        RequestID = "1003",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "52.75",
            AmountTendered = "52.75"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-001",
            PaymentTranID = "pmt-tran-abc123"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Purchase", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/Purchase')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'Purchase',
    RequestID: '1003',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '52.75',
      AmountTendered: '52.75'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-001',
      PaymentTranID: 'pmt-tran-abc123'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Purchase","RequestID":"1003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-001","PaymentTranID":"pmt-tran-abc123"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/Purchase"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode + Message.HostData.ReferenceID
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "Purchase",
        "RequestID": "1003",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "52.75",
            "AmountTendered": "52.75",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-001",
            "PaymentTranID": "pmt-tran-abc123",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Purchase", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}
⚠️ Persist ReferenceID and PaymentTranID

You must save ReferenceID from HostData and PaymentTranID from PaymentDetails to your database. Both are required if the server adds a tip via Adjustment later.

End the session

curl -X POST https://localhost:8600/PMI/EndPaymentSession \
  -H "Content-Type: application/json" \
  -H "X-ClientId: POS1" \
  --insecure \
  -d '{"Message":{"ClientID":"POS1","Operation":"EndPaymentSession",
    "RequestID":"1004","Version":"4.7.0",
    "PaymentDetails":{"SessionTranID":"ccl-session-001"}}}'
await fetch('https://localhost:8600/PMI/EndPaymentSession', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'EndPaymentSession',
    RequestID: '1004', Version: '4.7.0',
    PaymentDetails: { SessionTranID: sessionTranID }
  }})
});
requests.post(
    'https://localhost:8600/PMI/EndPaymentSession',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'EndPaymentSession',
          'RequestID': '1004', 'Version': '4.7.0',
          'PaymentDetails': {'SessionTranID': session_tran_id}}},
    verify=False
)
 [
        'ClientID' => 'POS1',
        'Operation' => 'EndPaymentSession',
        'RequestID' => '1004',
        'Version' => '4.7.0',
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-001'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/EndPaymentSession');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "EndPaymentSession",
        RequestID = "1004",
        Version = "4.7.0",
        PaymentDetails = new {
            SessionTranID = "ccl-session-001"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/EndPaymentSession", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/EndPaymentSession')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'EndPaymentSession',
    RequestID: '1004',
    Version: '4.7.0',
    PaymentDetails: {
      SessionTranID: 'ccl-session-001'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"EndPaymentSession","RequestID":"1004","Version":"4.7.0","PaymentDetails":{"SessionTranID":"ccl-session-001"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/EndPaymentSession"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success"
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "EndPaymentSession",
        "RequestID": "1004",
        "Version": "4.7.0",
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-001",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/EndPaymentSession", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Status"]}

Bar tab — OpenTab / CloseTab

🍺 Scenario: Store card on file, charge when guest closes out

The bar tab flow captures the card at arrival and stores it server-side. No charge occurs at OpenTab time. When the guest is ready to leave, CloseTab retrieves the stored card and charges the final amount including tip — no second card tap needed.

Step 1 — Open the tab (card capture, no charge)

# After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
curl -X POST https://localhost:8600/PMI/OpenTab \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "OpenTab",
      "RequestID": "2001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "PaymentDetails": { "SessionTranID": "ccl-session-bar-001" },
      "PosTranID": "pos-tran-001"
    }
  }'
# Response → Message.HostData.ReferenceID + Message.PaymentDetails.PaymentTranID  ⚠️ Save both for CloseTab
// After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
const response = await fetch('https://localhost:8600/PMI/OpenTab', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'OpenTab', RequestID: '2001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    PaymentDetails: { SessionTranID: sessionTranID },
    PosTranID: 'pos-tran-001'
  }})
});
const { Message } = await response.json();
const tabReferenceID  = Message.HostData.ReferenceID;           // ⚠️ Save for CloseTab
const tabPaymentTranID = Message.PaymentDetails.PaymentTranID;  // ⚠️ Save for CloseTab
# After BeginPaymentSession — OpenTab activates the PIN pad and captures the card:
resp = requests.post(
    'https://localhost:8600/PMI/OpenTab',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'OpenTab',
          'RequestID': '2001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'PaymentDetails': {'SessionTranID': session_tran_id},
          'PosTranID': 'pos-tran-001'}},
    verify=False
)
msg = resp.json()['Message']
tab_reference_id  = msg['HostData']['ReferenceID']          # Save for CloseTab
tab_payment_tran  = msg['PaymentDetails']['PaymentTranID']  # Save for CloseTab
 [
        'ClientID' => 'POS1',
        'Operation' => 'OpenTab',
        'RequestID' => '2001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-bar-001'
        ],
        'PosTranID' => 'pos-tran-001'
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/OpenTab');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$tabRef = $msg['HostData']['ReferenceID']; // save for CloseTab
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "OpenTab",
        RequestID = "2001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-bar-001"
        },
        PosTranID = "pos-tran-001"
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/OpenTab", payload);
var root = await resp.Content.ReadFromJsonAsync();
var tabRef = root.GetProperty("Message").GetProperty("HostData").GetProperty("ReferenceID").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/OpenTab')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'OpenTab',
    RequestID: '2001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-bar-001'
    },
    PosTranID: 'pos-tran-001'
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
tab_ref           = msg['HostData']['ReferenceID']              # save for CloseTab
tab_payment_tran  = msg['PaymentDetails']['PaymentTranID']      # save for CloseTab
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"OpenTab","RequestID":"2001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-session-bar-001"},"PosTranID":"pos-tran-001"}}""";  // Response: HostData.ReferenceID + PaymentDetails.PaymentTranID — save both for CloseTab

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/OpenTab"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.HostData.ReferenceID — save for CloseTab
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "OpenTab",
        "RequestID": "2001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-bar-001",
        },
        "PosTranID": "pos-tran-001",
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/OpenTab", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["HostData"].(map[string]any)["ReferenceID"]}
🍺 Store tab entry in your database

After OpenTab, save referenceId and paymentTranId against the table/guest record. You will need both to call CloseTab later — potentially after a lane restart or server shift change.

Step 2 — Close the tab (charge stored card)

# New BeginPaymentSession first (new SessionTranID), then:
curl -X POST https://localhost:8600/PMI/CloseTab \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "CloseTab",
      "RequestID": "2010", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "87.50", "AmountTendered": "87.50", "TipAmount": "15.00" },
      "HostData": { "ReferenceID": "tab-ref-xyz789" },
      "PaymentDetails": { "SessionTranID": "ccl-session-bar-002", "PaymentTranID": "pmt-tran-bar-abc" },
      "PosOptions": { "AllowAdjustment": false }
    }
  }'
// New BeginPaymentSession first, then:
const response = await fetch('https://localhost:8600/PMI/CloseTab', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'CloseTab', RequestID: '2010', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: finalAmount, AmountTendered: finalAmount, TipAmount: tipAmount },
    HostData: { ReferenceID: tabReferenceID },       // from OpenTab
    PaymentDetails: { SessionTranID: newSessionID, PaymentTranID: tabPaymentTranID },
    PosOptions: { AllowAdjustment: false }
  }})
});
const { Message } = await response.json();
resp = requests.post(
    'https://localhost:8600/PMI/CloseTab',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'CloseTab',
          'RequestID': '2010', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': final_amount, 'AmountTendered': final_amount, 'TipAmount': tip},
          'HostData': {'ReferenceID': tab_reference_id},
          'PaymentDetails': {'SessionTranID': new_session_id, 'PaymentTranID': tab_payment_tran_id},
          'PosOptions': {'AllowAdjustment': False}}},
    verify=False
)
 [
        'ClientID' => 'POS1',
        'Operation' => 'CloseTab',
        'RequestID' => '2010',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '87.50',
            'AmountTendered' => '87.50',
            'TipAmount' => '15.00'
        ],
        'HostData' => [
            'ReferenceID' => 'tab-ref-xyz789'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-bar-002',
            'PaymentTranID' => 'pmt-tran-bar-abc'
        ],
        'PosOptions' => [
            'AllowAdjustment' => false
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/CloseTab');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "CloseTab",
        RequestID = "2010",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "87.50",
            AmountTendered = "87.50",
            TipAmount = "15.00"
        },
        HostData = new {
            ReferenceID = "tab-ref-xyz789"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-bar-002",
            PaymentTranID = "pmt-tran-bar-abc"
        },
        PosOptions = new {
            AllowAdjustment = false
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/CloseTab", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/CloseTab')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'CloseTab',
    RequestID: '2010',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '87.50',
      AmountTendered: '87.50',
      TipAmount: '15.00'
    },
    HostData: {
      ReferenceID: 'tab-ref-xyz789'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-bar-002',
      PaymentTranID: 'pmt-tran-bar-abc'
    },
    PosOptions: {
      AllowAdjustment: false
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"CloseTab","RequestID":"2010","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"87.50","AmountTendered":"87.50","TipAmount":"15.00"},"HostData":{"ReferenceID":"tab-ref-xyz789"},"PaymentDetails":{"SessionTranID":"ccl-session-bar-002","PaymentTranID":"pmt-tran-bar-abc"},"PosOptions":{"AllowAdjustment":false}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/CloseTab"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "CloseTab",
        "RequestID": "2010",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "87.50",
            "AmountTendered": "87.50",
            "TipAmount": "15.00",
        },
        "HostData": map[string]any{
            "ReferenceID": "tab-ref-xyz789",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-bar-002",
            "PaymentTranID": "pmt-tran-bar-abc",
        },
        "PosOptions": map[string]any{
            "AllowAdjustment": false,
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/CloseTab", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}

Tip adjustment (post-authorization)

✏️ Scenario: Server adds tip after card is authorized

The guest signs the receipt and writes in a tip. The server enters the tip amount into the POS, which calls Adjustment to update the authorized amount. This is the most common tip flow for full-service restaurants.

Important: Adjustment requires the ReferenceID from the original Purchase response and the original PaymentTranID. Both must be stored after the initial payment.

# After a new BeginPaymentSession:
curl -X POST https://localhost:8600/PMI/Adjustment \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Adjustment",
      "RequestID": "3001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "62.75", "AmountTendered": "62.75", "TipAmount": "10.00" },
      "PaymentDetails": { "SessionTranID": "ccl-session-adj-001", "PaymentTranID": "pmt-tran-abc123" },
      "AdjustmentData": { "OriginalReferenceId": "ref-host-789xyz" }
    }
  }'
// After a new BeginPaymentSession:
const response = await fetch('https://localhost:8600/PMI/Adjustment', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'Adjustment', RequestID: '3001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: totalWithTip, AmountTendered: totalWithTip, TipAmount: tip },
    PaymentDetails: { SessionTranID: adjSessionID, PaymentTranID: origPaymentTranID },
    AdjustmentData: { OriginalReferenceId: origReferenceID }
  }})
});
const { Message } = await response.json();
resp = requests.post(
    'https://localhost:8600/PMI/Adjustment',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'Adjustment',
          'RequestID': '3001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': total_with_tip, 'AmountTendered': total_with_tip, 'TipAmount': tip},
          'PaymentDetails': {'SessionTranID': adj_session_id, 'PaymentTranID': orig_payment_tran_id},
          'AdjustmentData': {'OriginalReferenceId': orig_reference_id}}},
    verify=False
)
 [
        'ClientID' => 'POS1',
        'Operation' => 'Adjustment',
        'RequestID' => '3001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '62.75',
            'AmountTendered' => '62.75',
            'TipAmount' => '10.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-adj-001',
            'PaymentTranID' => 'pmt-tran-abc123'
        ],
        'AdjustmentData' => [
            'OriginalReferenceId' => 'ref-host-789xyz'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/Adjustment');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$authCode = $msg['PaymentDetails']['AuthCode'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "Adjustment",
        RequestID = "3001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "62.75",
            AmountTendered = "62.75",
            TipAmount = "10.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-adj-001",
            PaymentTranID = "pmt-tran-abc123"
        },
        AdjustmentData = new {
            OriginalReferenceId = "ref-host-789xyz"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Adjustment", payload);
var root = await resp.Content.ReadFromJsonAsync();
var authCode = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("AuthCode").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/Adjustment')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'Adjustment',
    RequestID: '3001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '62.75',
      AmountTendered: '62.75',
      TipAmount: '10.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-adj-001',
      PaymentTranID: 'pmt-tran-abc123'
    },
    AdjustmentData: {
      OriginalReferenceId: 'ref-host-789xyz'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
auth_code = msg['PaymentDetails']['AuthCode']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Adjustment","RequestID":"3001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"62.75","AmountTendered":"62.75","TipAmount":"10.00"},"PaymentDetails":{"SessionTranID":"ccl-session-adj-001","PaymentTranID":"pmt-tran-abc123"},"AdjustmentData":{"OriginalReferenceId":"ref-host-789xyz"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/Adjustment"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.AuthCode
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "Adjustment",
        "RequestID": "3001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "62.75",
            "AmountTendered": "62.75",
            "TipAmount": "10.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-adj-001",
            "PaymentTranID": "pmt-tran-abc123",
        },
        "AdjustmentData": map[string]any{
            "OriginalReferenceId": "ref-host-789xyz",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Adjustment", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["AuthCode"]}
FieldSourceNotes
AmountDueoriginal total + tipFull final amount charged to card
TipAmountserver inputTip only (not included in AmountDue — added to it)
PaymentTranIDoriginal Purchase responseMust match the original transaction
OriginalReferenceIdoriginal Purchase HostData.ReferenceIDGateway identifier for the original auth
SessionTranIDnew BeginPaymentSessionEach Adjustment needs a fresh session

Device tip prompt

📲 Scenario: Guest selects tip % on PIN pad before authorization

Instead of adding the tip post-authorization via Adjustment, you can prompt the guest to choose a tip percentage directly on the PIN pad during GetCardDetails. The PIN pad displays tip options (15%, 18%, 20%, Custom) and returns the total amount including tip.

curl -X POST https://localhost:8600/PMI/GetCardDetails \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "GetCardDetails",
      "RequestID": "4001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "52.75", "AmountTendered": "52.75" },
      "PaymentDetails": { "SessionTranID": "ccl-session-tip-001", "PromptTip": "YES" },
      "CardDetails": [{ "CardType": "Credit" }]
    }
  }'
# Response → Message.Amounts.TipAmount (guest-selected), .AmountTendered (total incl. tip)
const response = await fetch('https://localhost:8600/PMI/GetCardDetails', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'GetCardDetails', RequestID: '4001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: '52.75', AmountTendered: '52.75' },
    PaymentDetails: { SessionTranID: sessionTranID, PromptTip: 'YES' },
    CardDetails: [{ CardType: 'Credit' }]
  }})
});
const { Message } = await response.json();
const tip = Message.Amounts.TipAmount;       // guest-selected tip
const total = Message.Amounts.AmountTendered; // total incl. tip → pass to Purchase
resp = requests.post(
    'https://localhost:8600/PMI/GetCardDetails',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'GetCardDetails',
          'RequestID': '4001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': '52.75', 'AmountTendered': '52.75'},
          'PaymentDetails': {'SessionTranID': session_tran_id, 'PromptTip': 'YES'},
          'CardDetails': [{'CardType': 'Credit'}]}},
    verify=False
)
msg = resp.json()['Message']
tip = msg['Amounts']['TipAmount']
total = msg['Amounts']['AmountTendered']
 [
        'ClientID' => 'POS1',
        'Operation' => 'GetCardDetails',
        'RequestID' => '4001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '52.75',
            'AmountTendered' => '52.75'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-session-tip-001',
            'PromptTip' => 'YES'
        ],
        'CardDetails' => [[
            'CardType' => 'Credit'
        ]]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$tip = $msg['Amounts']['TipAmount'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "GetCardDetails",
        RequestID = "4001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "52.75",
            AmountTendered = "52.75"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-session-tip-001",
            PromptTip = "YES"
        },
        CardDetails = new[] {{'CardType': 'Credit'}}
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var tip = root.GetProperty("Message").GetProperty("Amounts").GetProperty("TipAmount").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'GetCardDetails',
    RequestID: '4001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '52.75',
      AmountTendered: '52.75'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-session-tip-001',
      PromptTip: 'YES'
    },
    CardDetails: [{'CardType': 'Credit'}]
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
tip = msg['Amounts']['TipAmount']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"4001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"52.75","AmountTendered":"52.75"},"PaymentDetails":{"SessionTranID":"ccl-session-tip-001","PromptTip":"YES"},"CardDetails":[{"CardType":"Credit"}]}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.TipAmount (guest-selected tip)
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "GetCardDetails",
        "RequestID": "4001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "52.75",
            "AmountTendered": "52.75",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-session-tip-001",
            "PromptTip": "YES",
        },
        "CardDetails": []any{{'CardType': 'Credit'}},
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["TipAmount"]}
Device tip vs Adjustment — which to use?

Device tip (PromptTip): Guest selects tip on PIN pad before the card is charged. Cleaner UX, no second API call needed. Best for counter service or quick-service restaurants.

Adjustment: Server adds tip after authorization, usually when the guest signs a paper slip and writes in a tip. Best for full-service dine-in where the check is left on the table.

Split tender

💳💵 Scenario: Multiple payment methods on one check

A guest pays part of the check with a gift card and the rest with a credit card. Each payment is a separate BeginPaymentSession → GetCardDetails → Purchase → EndPaymentSession cycle. Your POS tracks the remaining balance and presents the next payment form.

Each tender in a split payment is a fully independent PMI transaction. Run them sequentially:

  1. Payment 1 — BeginPaymentSession → GetCardDetails (CardType: GiftCard) → Purchase (AmountDue: gift card portion) → EndPaymentSession.
    Read Amounts.AmountApproved from the response. If less than AmountDue (partial approval), the remaining balance = AmountDue − AmountApproved.
  2. Payment 2 — BeginPaymentSession → GetCardDetails (CardType: Credit) → Purchase (AmountDue: remaining balance) → EndPaymentSession.
  3. Repeat for additional tenders until the check total is fully covered.
🍽️ Partial gift card approval

If the gift card has insufficient balance, the PMI Purchase response will return an AmountApproved less than AmountDue. Always compare these fields — do not assume full approval. Prompt for a second tender for the remaining balance.

Gift card as payment tender

🎁 Scenario: Pay (fully or partially) with a gift card

Gift card payments use the standard GetCardDetails → Purchase flow, but with CardType: "GiftCard" specified in the card details. The response includes CardBalance (remaining balance on the card after the transaction).

# Step 2: GetCardDetails — specify GiftCard type (after BeginPaymentSession)
curl -X POST https://localhost:8600/PMI/GetCardDetails \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "GetCardDetails",
      "RequestID": "1002", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "45.00", "AmountTendered": "45.00" },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-001" },
      "CardDetails": [{ "CardType": "GiftCard" }]
    }
  }'
# Step 3: Purchase with gift card flag
curl -X POST https://localhost:8600/PMI/Purchase \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Purchase",
      "RequestID": "1003", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "AmountDue": "45.00", "AmountTendered": "45.00" },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-001", "PaymentTranID": "pmt-gc-tran-001" },
      "PosOptions": { "CardType": "GiftCard" }
    }
  }'
# ⚠️ Check response: AmountApproved may be < AmountDue (partial approval)
# Response.Amounts.CardBalance = remaining gift card balance
// Step 2: GetCardDetails — GiftCard type (after BeginPaymentSession)
const gcdResp = await fetch('https://localhost:8600/PMI/GetCardDetails', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'GetCardDetails', RequestID: '1002', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: '45.00', AmountTendered: '45.00' },
    PaymentDetails: { SessionTranID: sessionTranID },
    CardDetails: [{ CardType: 'GiftCard' }]
  }})
});
const { Message: gcd } = await gcdResp.json();
const paymentTranID = gcd.PaymentDetails.PaymentTranID;

// Step 3: Purchase with GiftCard flag
const purResp = await fetch('https://localhost:8600/PMI/Purchase', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'Purchase', RequestID: '1003', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: '45.00', AmountTendered: '45.00' },
    PaymentDetails: { SessionTranID: sessionTranID, PaymentTranID: paymentTranID },
    PosOptions: { CardType: 'GiftCard' }
  }})
});
const { Message: pur } = await purResp.json();
// ⚠️ parseFloat(pur.Amounts.AmountApproved) < 45.00 → partial approval, prompt for another tender
const cardBalance = pur.Amounts.CardBalance;
# Step 2: GetCardDetails — GiftCard type
gcd = requests.post(
    'https://localhost:8600/PMI/GetCardDetails',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'GetCardDetails',
          'RequestID': '1002', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': '45.00', 'AmountTendered': '45.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id},
          'CardDetails': [{'CardType': 'GiftCard'}]}},
    verify=False
).json()['Message']
payment_tran_id = gcd['PaymentDetails']['PaymentTranID']

# Step 3: Purchase with GiftCard flag
pur = requests.post(
    'https://localhost:8600/PMI/Purchase',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'Purchase',
          'RequestID': '1003', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': '45.00', 'AmountTendered': '45.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id, 'PaymentTranID': payment_tran_id},
          'PosOptions': {'CardType': 'GiftCard'}}},
    verify=False
).json()['Message']
# ⚠️ float(pur['Amounts']['AmountApproved']) < 45.00 → partial, prompt for second tender
card_balance = pur['Amounts']['CardBalance']
 [
        'ClientID' => 'POS1',
        'Operation' => 'GetCardDetails',
        'RequestID' => '1002',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '45.00',
            'AmountTendered' => '45.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-001'
        ],
        'CardDetails' => [[
            'CardType' => 'GiftCard'
        ]]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/GetCardDetails');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$paymentTranID = $msg['PaymentDetails']['PaymentTranID'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "GetCardDetails",
        RequestID = "1002",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "45.00",
            AmountTendered = "45.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-001"
        },
        CardDetails = new[] {{'CardType': 'GiftCard'}}
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetCardDetails", payload);
var root = await resp.Content.ReadFromJsonAsync();
var paymentTranID = root.GetProperty("Message").GetProperty("PaymentDetails").GetProperty("PaymentTranID").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/GetCardDetails')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'GetCardDetails',
    RequestID: '1002',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '45.00',
      AmountTendered: '45.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-001'
    },
    CardDetails: [{'CardType': 'GiftCard'}]
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
payment_tran_id = msg['PaymentDetails']['PaymentTranID']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetCardDetails","RequestID":"1002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"45.00","AmountTendered":"45.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-001"},"CardDetails":[{"CardType":"GiftCard"}]}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/GetCardDetails"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.PaymentDetails.PaymentTranID
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "GetCardDetails",
        "RequestID": "1002",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "45.00",
            "AmountTendered": "45.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-001",
        },
        "CardDetails": []any{{'CardType': 'GiftCard'}},
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetCardDetails", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["PaymentDetails"].(map[string]any)["PaymentTranID"]}

Gift card management

All gift card management operations follow the same session pattern: BeginPaymentSession → operation → EndPaymentSession. The card is presented on the PIN pad unless a PAN/Track is explicitly provided.

Activate

🎁 Activate a new gift card
curl -X POST https://localhost:8600/PMI/Activate \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Activate",
      "RequestID": "5001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "ActivationAmount": "50.00" },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-act" },
      "PosOptions": { "CardType": "GiftCard" },
      "PosTranID": "pos-gc-001"
    }
  }'
# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "ACT445566" },
#   "Amounts": { "AmountApproved": "50.00", "CardBalance": "50.00" },
#   "CardDetails": [{ "LastFour": "4400", "CardName": "GiftCard" }] } }
const resp = await fetch('https://localhost:8600/PMI/Activate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'Activate', RequestID: '5001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { ActivationAmount: '50.00' },
    PaymentDetails: { SessionTranID: sessionTranID },
    PosOptions: { CardType: 'GiftCard' },
    PosTranID: 'pos-gc-001'
  }})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → '50.00'
// Message.CardDetails[0].LastFour → '4400'
result = requests.post(
    'https://localhost:8600/PMI/Activate',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'Activate',
          'RequestID': '5001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'ActivationAmount': '50.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id},
          'PosOptions': {'CardType': 'GiftCard'},
          'PosTranID': 'pos-gc-001'}},
    verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance']  # '50.00'
 [
        'ClientID' => 'POS1',
        'Operation' => 'Activate',
        'RequestID' => '5001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'ActivationAmount' => '50.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-act'
        ],
        'PosOptions' => [
            'CardType' => 'GiftCard'
        ],
        'PosTranID' => 'pos-gc-001'
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/Activate');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$balance = $msg['Amounts']['CardBalance'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "Activate",
        RequestID = "5001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            ActivationAmount = "50.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-act"
        },
        PosOptions = new {
            CardType = "GiftCard"
        },
        PosTranID = "pos-gc-001"
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Activate", payload);
var root = await resp.Content.ReadFromJsonAsync();
var balance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/Activate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'Activate',
    RequestID: '5001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      ActivationAmount: '50.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-act'
    },
    PosOptions: {
      CardType: 'GiftCard'
    },
    PosTranID: 'pos-gc-001'
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
balance = msg['Amounts']['CardBalance']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Activate","RequestID":"5001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ActivationAmount":"50.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-act"},"PosOptions":{"CardType":"GiftCard"},"PosTranID":"pos-gc-001"}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/Activate"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "Activate",
        "RequestID": "5001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "ActivationAmount": "50.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-act",
        },
        "PosOptions": map[string]any{
            "CardType": "GiftCard",
        },
        "PosTranID": "pos-gc-001",
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Activate", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}

BalanceInquiry

💰 Check remaining gift card balance
curl -X POST https://localhost:8600/PMI/BalanceInquiry \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "BalanceInquiry",
      "RequestID": "5002", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-bal" }
    }
  }'
# Response
# { "Message": { "StatusCode": 0,
#   "Amounts": { "CardBalance": "32.50" },
#   "CardDetails": [{ "LastFour": "4400" }] } }
const resp = await fetch('https://localhost:8600/PMI/BalanceInquiry', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'BalanceInquiry', RequestID: '5002', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    PaymentDetails: { SessionTranID: sessionTranID }
  }})
});
const { Message } = await resp.json();
const cardBalance = Message.Amounts.CardBalance; // e.g. '32.50'
result = requests.post(
    'https://localhost:8600/PMI/BalanceInquiry',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'BalanceInquiry',
          'RequestID': '5002', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'PaymentDetails': {'SessionTranID': session_tran_id}}},
    verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance']  # e.g. '32.50'
 [
        'ClientID' => 'POS1',
        'Operation' => 'BalanceInquiry',
        'RequestID' => '5002',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-bal'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/BalanceInquiry');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$balance = $msg['Amounts']['CardBalance'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "BalanceInquiry",
        RequestID = "5002",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-bal"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/BalanceInquiry", payload);
var root = await resp.Content.ReadFromJsonAsync();
var balance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/BalanceInquiry')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'BalanceInquiry',
    RequestID: '5002',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-bal'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
balance = msg['Amounts']['CardBalance']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"BalanceInquiry","RequestID":"5002","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-gc-session-bal"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/BalanceInquiry"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "BalanceInquiry",
        "RequestID": "5002",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-bal",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/BalanceInquiry", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}

Reload

🔄 Add funds to existing gift card
curl -X POST https://localhost:8600/PMI/Reload \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Reload",
      "RequestID": "5003", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "ReloadAmount": "25.00" },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-reload" }
    }
  }'
# Response — new balance returned
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "RLD778899" },
#   "Amounts": { "AmountApproved": "25.00", "CardBalance": "57.50" } } }
const resp = await fetch('https://localhost:8600/PMI/Reload', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'Reload', RequestID: '5003', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { ReloadAmount: '25.00' },
    PaymentDetails: { SessionTranID: sessionTranID }
  }})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → new balance (e.g. '57.50')
result = requests.post(
    'https://localhost:8600/PMI/Reload',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'Reload',
          'RequestID': '5003', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'ReloadAmount': '25.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id}}},
    verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance']  # new balance e.g. '57.50'
 [
        'ClientID' => 'POS1',
        'Operation' => 'Reload',
        'RequestID' => '5003',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'ReloadAmount' => '25.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-reload'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/Reload');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$newBalance = $msg['Amounts']['CardBalance'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "Reload",
        RequestID = "5003",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            ReloadAmount = "25.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-reload"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Reload", payload);
var root = await resp.Content.ReadFromJsonAsync();
var newBalance = root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/Reload')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'Reload',
    RequestID: '5003',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      ReloadAmount: '25.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-reload'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
new_balance = msg['Amounts']['CardBalance']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Reload","RequestID":"5003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ReloadAmount":"25.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-reload"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/Reload"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance (new balance after reload)
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "Reload",
        "RequestID": "5003",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "ReloadAmount": "25.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-reload",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Reload", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}

Deactivate

🚫 Cancel an activated gift card
curl -X POST https://localhost:8600/PMI/Deactivate \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "Deactivate",
      "RequestID": "5004", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": { "ActivationAmount": "50.00" },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-deact" }
    }
  }'
# Response
# { "Message": { "StatusCode": 0, "Amounts": { "CardBalance": "0.00" } } }
const resp = await fetch('https://localhost:8600/PMI/Deactivate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'Deactivate', RequestID: '5004', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { ActivationAmount: '50.00' },
    PaymentDetails: { SessionTranID: sessionTranID }
  }})
});
const { Message } = await resp.json();
// Message.Amounts.CardBalance → '0.00'
result = requests.post(
    'https://localhost:8600/PMI/Deactivate',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'Deactivate',
          'RequestID': '5004', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'ActivationAmount': '50.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id}}},
    verify=False
).json()['Message']
card_balance = result['Amounts']['CardBalance']  # '0.00'
 [
        'ClientID' => 'POS1',
        'Operation' => 'Deactivate',
        'RequestID' => '5004',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'ActivationAmount' => '50.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-deact'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/Deactivate');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Amounts']['CardBalance'] === '0.00'
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "Deactivate",
        RequestID = "5004",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            ActivationAmount = "50.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-deact"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/Deactivate", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Amounts").GetProperty("CardBalance").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/Deactivate')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'Deactivate',
    RequestID: '5004',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      ActivationAmount: '50.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-deact'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
# msg['Amounts']['CardBalance'] == '0.00'
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"Deactivate","RequestID":"5004","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"ActivationAmount":"50.00"},"PaymentDetails":{"SessionTranID":"ccl-gc-session-deact"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/Deactivate"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.CardBalance === "0.00"
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "Deactivate",
        "RequestID": "5004",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "ActivationAmount": "50.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-deact",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/Deactivate", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["CardBalance"]}

CashOut

💵 Cash out remaining gift card balance
curl -X POST https://localhost:8600/PMI/CashOut \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "CashOut",
      "RequestID": "5005", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "PaymentDetails": { "SessionTranID": "ccl-gc-session-cashout" },
      "PosOptions": { "TranType": "CashOut" }
    }
  }'
# Response — balance dispensed as cash
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "CSH334455" },
#   "Amounts": { "AmountApproved": "57.50", "CardBalance": "0.00" } } }
const resp = await fetch('https://localhost:8600/PMI/CashOut', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'CashOut', RequestID: '5005', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    PaymentDetails: { SessionTranID: sessionTranID },
    PosOptions: { TranType: 'CashOut' }
  }})
});
const { Message } = await resp.json();
// Message.Amounts.AmountApproved → amount cashed out
// Message.Amounts.CardBalance → '0.00'
result = requests.post(
    'https://localhost:8600/PMI/CashOut',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'CashOut',
          'RequestID': '5005', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'PaymentDetails': {'SessionTranID': session_tran_id},
          'PosOptions': {'TranType': 'CashOut'}}},
    verify=False
).json()['Message']
amount_cashed_out = result['Amounts']['AmountApproved']
card_balance = result['Amounts']['CardBalance']  # '0.00'
 [
        'ClientID' => 'POS1',
        'Operation' => 'CashOut',
        'RequestID' => '5005',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-gc-session-cashout'
        ],
        'PosOptions' => [
            'TranType' => 'CashOut'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/CashOut');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$dispensed = $msg['Amounts']['AmountApproved'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "CashOut",
        RequestID = "5005",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        PaymentDetails = new {
            SessionTranID = "ccl-gc-session-cashout"
        },
        PosOptions = new {
            TranType = "CashOut"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/CashOut", payload);
var root = await resp.Content.ReadFromJsonAsync();
var dispensed = root.GetProperty("Message").GetProperty("Amounts").GetProperty("AmountApproved").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/CashOut')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'CashOut',
    RequestID: '5005',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    PaymentDetails: {
      SessionTranID: 'ccl-gc-session-cashout'
    },
    PosOptions: {
      TranType: 'CashOut'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
dispensed = msg['Amounts']['AmountApproved']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"CashOut","RequestID":"5005","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"PaymentDetails":{"SessionTranID":"ccl-gc-session-cashout"},"PosOptions":{"TranType":"CashOut"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/CashOut"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Amounts.AmountApproved (cash dispensed), CardBalance → "0.00"
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "CashOut",
        "RequestID": "5005",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-gc-session-cashout",
        },
        "PosOptions": map[string]any{
            "TranType": "CashOut",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/CashOut", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Amounts"].(map[string]any)["AmountApproved"]}

IncrementalAuth

📈 Scenario: Secure additional authorization on an open tab

For bar tabs with high expected spending, you can add incremental authorizations before closing. Each IncrementalAuth extends the secured amount. Finalize with IncrementalCompletion.

curl -X POST https://localhost:8600/PMI/IncrementalAuth \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "IncrementalAuth",
      "RequestID": "7001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 },
      "Amounts": {
        "AmountDue": "50.00",
        "AmountTendered": "50.00",
        "TotalAmountTendered": "150.00"
      },
      "PaymentDetails": { "SessionTranID": "ccl-inc-session-001" },
      "CardDetails": [{ "CardType": "Credit" }],
      "AdjustmentData": { "OriginalReferenceId": "tab-ref-xyz789" }
    }
  }'
# Response
# { "Message": { "StatusCode": 0, "PaymentDetails": { "AuthCode": "INC223344" },
#   "HostData": { "ReferenceID": "inc-ref-new-001" },
#   "Amounts": { "AmountApproved": "50.00", "TotalAmountTendered": "150.00" } } }
const resp = await fetch('https://localhost:8600/PMI/IncrementalAuth', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'IncrementalAuth', RequestID: '7001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 },
    Amounts: { AmountDue: '50.00', AmountTendered: '50.00', TotalAmountTendered: '150.00' },
    PaymentDetails: { SessionTranID: sessionTranID },
    CardDetails: [{ CardType: 'Credit' }],
    AdjustmentData: { OriginalReferenceId: originalReferenceId }
  }})
});
const { Message } = await resp.json();
const newReferenceId = Message.HostData.ReferenceID;
result = requests.post(
    'https://localhost:8600/PMI/IncrementalAuth',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'IncrementalAuth',
          'RequestID': '7001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1},
          'Amounts': {'AmountDue': '50.00', 'AmountTendered': '50.00', 'TotalAmountTendered': '150.00'},
          'PaymentDetails': {'SessionTranID': session_tran_id},
          'CardDetails': [{'CardType': 'Credit'}],
          'AdjustmentData': {'OriginalReferenceId': original_reference_id}}},
    verify=False
).json()['Message']
new_reference_id = result['HostData']['ReferenceID']
 [
        'ClientID' => 'POS1',
        'Operation' => 'IncrementalAuth',
        'RequestID' => '7001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ],
        'Amounts' => [
            'AmountDue' => '50.00',
            'AmountTendered' => '50.00',
            'TotalAmountTendered' => '150.00'
        ],
        'PaymentDetails' => [
            'SessionTranID' => 'ccl-inc-session-001'
        ],
        'CardDetails' => [[
            'CardType' => 'Credit'
        ]],
        'AdjustmentData' => [
            'OriginalReferenceId' => 'tab-ref-xyz789'
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/IncrementalAuth');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
$newRef = $msg['HostData']['ReferenceID'];
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "IncrementalAuth",
        RequestID = "7001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        },
        Amounts = new {
            AmountDue = "50.00",
            AmountTendered = "50.00",
            TotalAmountTendered = "150.00"
        },
        PaymentDetails = new {
            SessionTranID = "ccl-inc-session-001"
        },
        CardDetails = new[] {{'CardType': 'Credit'}},
        AdjustmentData = new {
            OriginalReferenceId = "tab-ref-xyz789"
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/IncrementalAuth", payload);
var root = await resp.Content.ReadFromJsonAsync();
var newRef = root.GetProperty("Message").GetProperty("HostData").GetProperty("ReferenceID").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/IncrementalAuth')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'IncrementalAuth',
    RequestID: '7001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    },
    Amounts: {
      AmountDue: '50.00',
      AmountTendered: '50.00',
      TotalAmountTendered: '150.00'
    },
    PaymentDetails: {
      SessionTranID: 'ccl-inc-session-001'
    },
    CardDetails: [{'CardType': 'Credit'}],
    AdjustmentData: {
      OriginalReferenceId: 'tab-ref-xyz789'
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
new_ref = msg['HostData']['ReferenceID']
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"IncrementalAuth","RequestID":"7001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1},"Amounts":{"AmountDue":"50.00","AmountTendered":"50.00","TotalAmountTendered":"150.00"},"PaymentDetails":{"SessionTranID":"ccl-inc-session-001"},"CardDetails":[{"CardType":"Credit"}],"AdjustmentData":{"OriginalReferenceId":"tab-ref-xyz789"}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/IncrementalAuth"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.HostData.ReferenceID (new reference for further incremental auths)
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "IncrementalAuth",
        "RequestID": "7001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
        "Amounts": map[string]any{
            "AmountDue": "50.00",
            "AmountTendered": "50.00",
            "TotalAmountTendered": "150.00",
        },
        "PaymentDetails": map[string]any{
            "SessionTranID": "ccl-inc-session-001",
        },
        "CardDetails": []any{{'CardType': 'Credit'}},
        "AdjustmentData": map[string]any{
            "OriginalReferenceId": "tab-ref-xyz789",
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/IncrementalAuth", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["HostData"].(map[string]any)["ReferenceID"]}

Device notifications

During GetCardDetails, the PIN pad displays prompts to the customer ("Tap Card", "Insert Chip", etc.). Your POS application should poll GetNotifications in a background loop to receive these messages and display them to the server/cashier.

StartNotifications

# Call before GetCardDetails to activate the PIN pad display
curl -X POST https://localhost:8600/PMI/StartNotifications \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "StartNotifications",
      "RequestID": "9001", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
    }
  }'
# Response
# { "Message": { "StatusCode": 0, "StatusMessage": "Notifications started" } }
// Call before GetCardDetails to activate the PIN pad display
const resp = await fetch('https://localhost:8600/PMI/StartNotifications', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'StartNotifications', RequestID: '9001', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
  }})
});
const { Message } = await resp.json();
// Message.StatusMessage → 'Notifications started'
# Call before GetCardDetails to activate the PIN pad display
result = requests.post(
    'https://localhost:8600/PMI/StartNotifications',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'StartNotifications',
          'RequestID': '9001', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}}},
    verify=False
).json()['Message']
# result['StatusMessage'] → 'Notifications started'
 [
        'ClientID' => 'POS1',
        'Operation' => 'StartNotifications',
        'RequestID' => '9001',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/StartNotifications');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "StartNotifications",
        RequestID = "9001",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/StartNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/StartNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'StartNotifications',
    RequestID: '9001',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"StartNotifications","RequestID":"9001","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/StartNotifications"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success"
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "StartNotifications",
        "RequestID": "9001",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/StartNotifications", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Status"]}

GetNotifications

Poll this endpoint repeatedly while waiting for the card. It returns PIN pad display messages (e.g., "Tap Card", "Please Wait") as they occur. The timeout query parameter (seconds) holds the connection open until a notification is available — an empty body means nothing is queued yet, which is normal.

curl -X POST "https://localhost:8600/PMI/GetNotifications?timeout=3" \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "GetNotifications",
      "RequestID": "9002", "Version": "4.7.0"
    }
  }'
# Response — notification available
# { "Notification": [
#     { "CustomerText": "Please tap, insert, or swipe your card" },
#     { "CustomerText": "Processing..." }
#   ] }
# Response — nothing queued (empty body or empty array — normal, keep polling)
// Poll in a loop while waiting for GetCardDetails to return
async function pollNotifications(sessionActive) {
  while (sessionActive()) {
    const resp = await fetch('https://localhost:8600/PMI/GetNotifications?timeout=3', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
      body: JSON.stringify({ Message: {
        ClientID: 'POS1', Operation: 'GetNotifications', RequestID: '9002', Version: '4.7.0'
      }})
    });
    const data = await resp.json().catch(() => null);
    if (data?.Notification?.length) {
      data.Notification.forEach(n => displayMessage(n.CustomerText));
    }
  }
}
# Poll in a loop while waiting for GetCardDetails to return
import threading

def poll_notifications(stop_event):
    while not stop_event.is_set():
        try:
            resp = requests.post(
                'https://localhost:8600/PMI/GetNotifications',
                params={'timeout': 3},
                headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
                json={'Message': {'ClientID': 'POS1', 'Operation': 'GetNotifications',
                      'RequestID': '9002', 'Version': '4.7.0'}},
                verify=False, timeout=5
            ).json()
            for n in resp.get('Notification', []):
                print(n.get('CustomerText', ''))
        except Exception:
            pass
 [
        'ClientID' => 'POS1',
        'Operation' => 'GetNotifications',
        'RequestID' => '9002',
        'Version' => '4.7.0'
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/GetNotifications');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $data['Notification'] — array of customer-facing strings
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "GetNotifications",
        RequestID = "9002",
        Version = "4.7.0"
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/GetNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Notification").EnumerateArray()
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/GetNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'GetNotifications',
    RequestID: '9002',
    Version: '4.7.0'
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
# data['Notification']  # array of customer-facing strings
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"GetNotifications","RequestID":"9002","Version":"4.7.0"}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/GetNotifications"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Parse resp.body() → Notification[] array
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "GetNotifications",
        "RequestID": "9002",
        "Version": "4.7.0",
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/GetNotifications", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Notification"].([]any)}

StopNotifications

# Call after card is captured (GetCardDetails returns)
curl -X POST https://localhost:8600/PMI/StopNotifications \
  -H "Content-Type: application/json" -H "X-ClientId: POS1" --insecure \
  -d '{
    "Message": {
      "ClientID": "POS1", "Operation": "StopNotifications",
      "RequestID": "9003", "Version": "4.7.0",
      "ConfigOptions": { "CompanyNumber": 185197, "StoreNumber": 1, "LaneNumber": 1 }
    }
  }'
# Response
# { "Message": { "StatusCode": 0, "StatusMessage": "Notifications stopped" } }
// Call after card is captured (GetCardDetails returns)
const resp = await fetch('https://localhost:8600/PMI/StopNotifications', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-ClientId': 'POS1' },
  body: JSON.stringify({ Message: {
    ClientID: 'POS1', Operation: 'StopNotifications', RequestID: '9003', Version: '4.7.0',
    ConfigOptions: { CompanyNumber: 185197, StoreNumber: 1, LaneNumber: 1 }
  }})
});
const { Message } = await resp.json();
// Message.StatusMessage → 'Notifications stopped'
# Call after card is captured (GetCardDetails returns)
result = requests.post(
    'https://localhost:8600/PMI/StopNotifications',
    headers={'Content-Type': 'application/json', 'X-ClientId': 'POS1'},
    json={'Message': {'ClientID': 'POS1', 'Operation': 'StopNotifications',
          'RequestID': '9003', 'Version': '4.7.0',
          'ConfigOptions': {'CompanyNumber': 185197, 'StoreNumber': 1, 'LaneNumber': 1}}},
    verify=False
).json()['Message']
# result['StatusMessage'] → 'Notifications stopped'
 [
        'ClientID' => 'POS1',
        'Operation' => 'StopNotifications',
        'RequestID' => '9003',
        'Version' => '4.7.0',
        'ConfigOptions' => [
            'CompanyNumber' => 185197,
            'StoreNumber' => 1,
            'LaneNumber' => 1
        ]
    ]
]);

$ch = curl_init('https://localhost:8600/PMI/StopNotifications');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'X-ClientId: POS1'],
    CURLOPT_POSTFIELDS => $body,
]);
$msg = json_decode(curl_exec($ch), true)['Message'];
curl_close($ch);
// $msg['Status'] === 'Success'
using System.Net.Http.Json;

var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (_, _, _, _) => true };
using var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("X-ClientId", "POS1");

var payload = new {
    Message = new {
        ClientID = "POS1",
        Operation = "StopNotifications",
        RequestID = "9003",
        Version = "4.7.0",
        ConfigOptions = new {
            CompanyNumber = 185197,
            StoreNumber = 1,
            LaneNumber = 1
        }
    }
};
var resp = await client.PostAsJsonAsync("https://localhost:8600/PMI/StopNotifications", payload);
var root = await resp.Content.ReadFromJsonAsync();
// root.GetProperty("Message").GetProperty("Status").GetString();
require 'net/http'
require 'json'
require 'openssl'

uri = URI('https://localhost:8600/PMI/StopNotifications')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE  # dev only

req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['X-ClientId'] = 'POS1'
req.body = {
  Message: {
    ClientID: 'POS1',
    Operation: 'StopNotifications',
    RequestID: '9003',
    Version: '4.7.0',
    ConfigOptions: {
      CompanyNumber: 185197,
      StoreNumber: 1,
      LaneNumber: 1
    }
  }
}.to_json

msg = JSON.parse(http.request(req).body)['Message']
# msg['Status'] == 'Success'
import java.net.http.*;
import java.net.URI;

HttpClient client = HttpClient.newBuilder()
    .sslContext(buildTrustAllContext())  // see pmi-integration guide
    .build();

String jsonBody = """{"Message":{"ClientID":"POS1","Operation":"StopNotifications","RequestID":"9003","Version":"4.7.0","ConfigOptions":{"CompanyNumber":185197,"StoreNumber":1,"LaneNumber":1}}}""";

HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://localhost:8600/PMI/StopNotifications"))
    .header("Content-Type", "application/json")
    .header("X-ClientId", "POS1")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// Message.Status === "Success"
package main

import (
    "bytes"; "crypto/tls"; "encoding/json"; "net/http"
)

func main() {
    tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
    client := &http.Client{Transport: tr}

    body, _ := json.Marshal(map[string]any{
    "Message": map[string]any{
        "ClientID": "POS1",
        "Operation": "StopNotifications",
        "RequestID": "9003",
        "Version": "4.7.0",
        "ConfigOptions": map[string]any{
            "CompanyNumber": 185197,
            "StoreNumber": 1,
            "LaneNumber": 1,
        },
    },
})
    req, _ := http.NewRequest("POST", "https://localhost:8600/PMI/StopNotifications", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-ClientId", "POS1")

    resp, _ := client.Do(req)
    var result map[string]any
    json.NewDecoder(resp.Body).Decode(&result)
    // result["Message"].(map[string]any)["Status"]}

API quick reference

OperationEndpointRestaurant useSession required
BeginPaymentSession POST /BeginPaymentSession Opens every payment flow Creates session
EndPaymentSession POST /EndPaymentSession Closes every payment flow Closes session
GetCardDetails POST /GetCardDetails Card capture (credit, debit, gift); add PromptTip: "YES" for device tip ✓ Required
Purchase POST /Purchase Standard payment & gift card tender ✓ Required
OpenTab POST /OpenTab Store card for bar tab (no charge) ✓ Required
CloseTab POST /CloseTab Charge stored bar tab card (with tip) ✓ New session
Adjustment POST /Adjustment Add tip after authorization ✓ New session
IncrementalAuth POST /IncrementalAuth Extend bar tab authorization limit ✓ Required
IncrementalCompletion POST /IncrementalCompletion Finalize incremental auth chain ✓ Required
Activate POST /Activate Activate new gift card ✓ Required
BalanceInquiry POST /BalanceInquiry Check gift card balance ✓ Required
Reload POST /Reload Add funds to gift card ✓ Required
Deactivate POST /Deactivate Cancel/deactivate gift card ✓ Required
CashOut POST /CashOut Cash out remaining gift card balance ✓ Required
StartNotifications POST /StartNotifications Activate PIN pad display & message queue Optional
GetNotifications POST /GetNotifications?timeout=3 Poll PIN pad prompts during card wait Optional
StopNotifications POST /StopNotifications Stop notification queue after card captured Optional

Status codes

StatusCodeMeaningAction
0Success / ApprovedProceed
1General errorShow error, EndPaymentSession
2DeclinedPrompt guest for alternate tender
3Partial approvalCollect remaining balance with another tender
4Card removed during readAsk guest to re-present card
5TimeoutCancel and EndPaymentSession
6Communication errorRetry or EndPaymentSession
7Session not foundEndPaymentSession, start new session
8Duplicate requestCheck if previous request succeeded
9Engine not initializedCall Initialize → OpenLane first
10Lane not openCall OpenLane

Best practices

Persisting transaction data

For every approved Purchase, OpenTab, or CloseTab, persist these fields to your database immediately — before EndPaymentSession:

  • HostData.ReferenceID — required for Adjustment, IncrementalAuth, IncrementalCompletion
  • PaymentDetails.PaymentTranID — required for Adjustment and CloseTab
  • PaymentDetails.AuthCode — for receipts and audit trail
  • CardDetails.CardBrand + LastFour — for receipts and chargebacks

Error handling

Check StatusCode in every PMI response. Non-zero codes require action before proceeding:

StatusCodeMeaningRequired action
2 — DeclinedIssuer declined the cardCall EndPaymentSession, then prompt for alternate tender
3 — PartialAmountApproved < AmountDueCall EndPaymentSession, collect remaining balance with second tender
4 — Card removedCard pulled out mid-readCall EndPaymentSession, ask guest to re-present card
5 — TimeoutNo card presented in timeCall EndPaymentSession, retry or cancel
6 — Comm errorNetwork/gateway unreachableRetry once; if still failing, call EndPaymentSession
Any non-zeroSee status codes tableAlways call EndPaymentSession before opening a new session

Receipt content for restaurants

A compliant restaurant receipt must include:

  • Table number, server name, check number, date & time
  • Itemized list with seat assignment (for split checks)
  • Subtotal, tax, tip (if included), total
  • Payment method: card brand + last four digits
  • PAYMENT AUTH: authorization code from Purchase or CloseTab
  • TIP ADJ AUTH: adjustment auth code if tip was added post-authorization
  • For gift cards: CardBalance remaining after transaction