Payments switch · direct integration

Cygma API

A direct JSON gateway to Cygma – sale, pre-auth, completion, refund, reversal, balance inquiry, tokenization, and batch settlement, all over plain HTTPS. Built for cart vendors, kiosks, VAR integrations, and anywhere a merchant needs to authorize a card payment without routing through an intermediate gateway.

Overview

Introduction

Cygma is a full ISO 8583 payments switch wrapped in a JSON API. One bearer credential per terminal, one POST per transaction, standard ISO response codes that mean exactly what the spec says they mean.

Every endpoint is a POST with a JSON body. The body shape is the same across every transaction type – only the MessageType, ProcessingCode, and the URL path change.

  • Production: https://api.cygma.com:443
  • Cert sandbox: https://apicert-sandbox.cygma.com:9443
3 steps

Quickstart

  1. Get your CardAcquirerId, TerminalId, and SecurityControlInformation from EPI. The first two go in plaintext per request; the third is the auth secret.
  2. POST your transaction JSON to the matching URL (one URL per transaction type – see Sale below for the canonical shape).
  3. Read ResponseCode from the response. "00" means approved; anything else, look up in Response codes.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/saledebitebt/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "200",
  "PrimaryAccountNumber": "4005529091234562",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000000400",
  "LocalTransactionTime": "103200",
  "LocalTransactionDate": "04/09",
  "SystemsTraceNumber": "599220",
  "ExpirationDate": "31/12",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>"
}'
Credentials

Authentication

Three fields identify the request – all live in the JSON body:

  • CardAcquirerId (DE 42) – your parent merchant number, assigned by EPI when the merchant is provisioned.
  • TerminalId (DE 41) – unique per merchant. EPI assigns this when the merchant is provisioned.
  • SecurityControlInformation (DE 53) – the per-terminal secret. Cygma validates the (acquirer, SCI) pair on every call and declines if either doesn’t match what they have stored. Treat it like a bearer token.

TLS terminates at the Cygma edge. There’s no Authorization header – credentials travel in the body.

↑ Request – what you send
// Required on EVERY request
{
  "CardAcquirerId":             "<YOUR_CARD_ACQUIRER_ID>",   // DE 42, assigned by EPI
  "TerminalId":                 "<TERMINAL_ID>",     // per-merchant, DE 41
  "SecurityControlInformation": "<SCI>"              // DE 53 – secret, never expose
}
Every request

Message envelope

Every transaction uses the same outer shape. A few details that aren’t obvious from the spec PDFs:

  • MessageType is the 3-digit form ("200"), not the canonical ISO four-digit MTI. "0200" returns a generic ErrorCode: 100000 / "Server error" without ever reaching the ISO layer.
  • LocalTransactionDate and ExpirationDate need slashes on input (MM/DD, YY/MM). The gateway echoes back the no-slash form in responses but rejects unsigned input.
  • TransactionAmount is 12 digits, zero-padded, in cents (minor units). "000000000400" = $4.00.
  • SystemsTraceNumber (STAN) must be unique per terminal per business day. Reversals match on (terminal, STAN), so keep it stable across retries of the same intent.
  • Most optional/conditional fields live inside RequestDataElements – Cygma calls this the “Private Use” data element.
FieldTypeDescription
MessageTyperequiredstring3-digit MTI (e.g. '200') – not the 4-digit ISO form.
ProcessingCoderequiredstring6 digits; selects the transaction type.
TransactionAmountrequiredstring12 digits, zero-padded, in cents. '000000000400' = $4.00.
LocalTransactionTimerequiredstringLocal time as hhmmss.
LocalTransactionDaterequiredstringMM/DD, with a slash on input.
SystemsTraceNumberrequiredstring6-digit STAN, unique per terminal per business day.
ExpirationDaterequiredstringCard expiry as YY/MM, with a slash on input.
POSEntryModerequiredstringHow the card was captured (DE 22).
POSConditionCoderequiredstringPOS condition (DE 25).
TerminalIdrequiredstringPer-merchant terminal id (DE 41), assigned by EPI.
CardAcquirerIdrequiredstringAcquirer / parent merchant number (DE 42), assigned by EPI.
SecurityControlInformationrequiredstringPer-terminal secret (DE 53). Treat like a bearer token.
PrimaryAccountNumberrequiredstringCard number (PAN). Omit when sending track or token data.
RequestDataElementsoptionalobjectOptional / conditional fields – Cygma's 'Private Use' element.
RequestDataElements.InvoiceReferenceNumberoptionalstringYour order / invoice reference.
RequestDataElements.HardwareVendorIdentifieroptionalstringTerminal hardware vendor id.
RequestDataElements.SoftwareIdentifieroptionalstringIntegration software id.
RequestDataElements.CardTypeoptionalstringCard type hint (e.g. 'CR').
RequestDataElements.ElectronicCommerceIndicatorrequiredstring'01' on every card-not-present transaction. Any other value downgrades the interchange.
↑ Request – what you send
{
  "MessageType": "200",                            // 3-digit MTI
  "ProcessingCode": "000000",                      // 6 digits
  "TransactionAmount": "000000000400",             // 12 digits, cents
  "LocalTransactionTime": "103200",                // hhmmss
  "LocalTransactionDate": "04/09",                 // MM/DD with slash
  "SystemsTraceNumber": "599220",                  // 6-digit STAN
  "ExpirationDate": "31/12",                       // YY/MM with slash
  "POSEntryMode": "012",                           // see DE 22 - keyed, CNP
  "POSConditionCode": "08",                        // see DE 25 - MOTO / CNP
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "PrimaryAccountNumber": "4005529091234562",
  "RequestDataElements": {
    "InvoiceReferenceNumber":     "ORDER-1042",
    "HardwareVendorIdentifier":   "F150",
    "SoftwareIdentifier":         "1013",
    "CardType":                   "CR",
    "ElectronicCommerceIndicator": "01"
  }
}
Qualify the transaction right

POS entry + condition codes

The (POSEntryMode, POSConditionCode) pair tells Cygma how the card data got into the terminal. Picking the right pair matters – it drives interchange qualification, chargeback liability, and (for some processors) whether the transaction downgrades to a higher-cost tier.

ScenarioPOSEntryModePOSConditionCode
EMV chip read05100
Contactless / Apple Pay / Google Pay07100
Swipe (EMV fallback)80100
Key-entered, card present01171
Key-entered, CNP (MOTO)01208

CNP also needs the e-commerce indicator. Send RequestDataElements.ElectronicCommerceIndicator: "01" on every key-entered card-not-present transaction. Without it the transaction downgrades.

Implementation note. The table above is the specification. Send 012/08 on keyed CNP and test the pair on its own; a code 30 format error on such a packet points to RequestDataElements, not to the entry-mode pair.

Misclassifying a CNP transaction as card-present (or vice versa) can cost the merchant interchange and expose them to chargebacks they wouldn’t otherwise face. When in doubt, pick the more conservative classification.

↑ Request – what you send
// EMV chip read
{ "POSEntryMode": "051", "POSConditionCode": "00" }

// Contactless (Apple/Google Pay, tap)
{ "POSEntryMode": "071", "POSConditionCode": "00" }

// Magstripe swipe — EMV fallback
{ "POSEntryMode": "801", "POSConditionCode": "00" }

// Key-entered, card + cardholder present
{ "POSEntryMode": "011", "POSConditionCode": "71" }

// Key-entered, cardholder NOT present (MOTO / CNP).
// Per the Cygma POS Entry Mode + Condition Code spec:
{ "POSEntryMode": "012", "POSConditionCode": "08" }

// CNP also requires the e-commerce indicator, or the
// transaction downgrades:
{ "RequestDataElements": { "ElectronicCommerceIndicator": "01" } }
one URL per transaction

Transaction types

Every transaction type has its own dedicated URL under /hostapi/ – the JSON envelope is the same shape everywhere; only the URL (and a couple of type-specific fields) changes. The everyday set is documented in depth below; the rest are listed here so you know the full vocabulary.

PathWhat it does
/saledebitebt/The everyday sale: authorize and store in the terminal batch for end-of-day settlement. Chip, tap, swipe, or keyed.
/mailorder/Card-not-present sale – e-commerce and phone orders. Same envelope, CNP condition coding and ECI.
/preauthorization/ + /completion/Hold now, capture later. Pre-auth places the hold (fuel, hospitality, bar tabs); Completion settles it once the final amount is known – tips included.
/authorization/An 0100 auth-only: obtain an approval that is NOT stored in the batch. Follow with an advice at fulfilment, or a reversal to release the hold if the order can’t ship.
/onlinerefund/Refund a cardholder when the original isn’t in the open batch – host-approved before the terminal journals it.
/offlinerefund/ · /offlinesale/Offline entries: refunds processed off-line, tip-adjusted completions, and voice-authorized (IVR) sales pushed into the batch after the fact.
/reversalfull/Void / timeout reversal. Sent when no valid auth response arrived in 35–45s (retry up to 3×), or to undo a same-batch transaction.
/reversalpartial/Partial reversal – release the difference when the final sale is less than the authorized amount (lodging, fuel).
/adjustsale/ · /adjustcredit/Adjustments – notify the host the amount of a prior sale/credit changed (tip adjust being the classic).
/cardverification/Zero-dollar verification – confirm a card is live (AVS/CVV probe) before you know the real amount.
/balanceinquiry/ · /availablefunds/Balance lookups – gift/debit card balance, or available funds on a debit/credit card.
/cashadvance/ · /salecash/Cash transactions – cash advance against a card’s cash limit; cash-back sales.
/payment/Payment (bill-pay style credit to a card account).
/settlementrequest/ · /settlementtrailer/ · /batchupload/End of day. Settlement Request compares terminal vs host totals; on mismatch (code 95) the terminal uploads each capture via Batch Upload, then closes with Settlement Trailer.
POST /hostapi/saledebitebt/

Sale

MTI 200, ProcessingCode 000000. The canonical card-present sale – terminal swipes / chip-reads / key-enters, terminal sends, gateway authorizes, transaction is stored in the terminal batch for end-of-day settlement.

Three ways to present the card (send exactly one): a keyed PrimaryAccountNumber + ExpirationDate (shown here), raw magstripe Track1Data / Track2Data (DE 45 / DE 35), or an EMV chip/contactless read via ICCSystemRelatedData (DE 55) – see Card entry: MSR / EMV for the track + EMV payload shapes and the matching POSEntryMode values.

For cardholder-not-present (e-commerce, MOTO), use /hostapi/mailorder/ instead – it sets POSConditionCode: "59" and expects ElectronicCommerceIndicator.

Read AuthorizationIdResponse – that’s the auth code you store on your order for future refund / reversal lookups.

Sending CVV2RequestValue or the billing ZipCode / Address? Those aren’t free-form – see CVV2 verification and AVS verification for the exact field formats and the result-code tables.

FieldTypeDescription
MessageTyperequiredstringAlways '200' for a sale.
ProcessingCoderequiredstring'000000' – debit sale.
PrimaryAccountNumberrequiredstringCard number (PAN).
RequestDataElements.InvoiceReferenceNumberoptionalstringYour order reference, stored on the transaction.
RequestDataElements.ZipCodeoptionalstringBilling ZIP – its presence triggers AVS. See AVS verification.
RequestDataElements.CVV2RequestValueoptionalstring6-character CVV2 control field. See CVV2 verification.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/saledebitebt/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "200",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000001500",
  "LocalTransactionTime": "144530",
  "LocalTransactionDate": "06/09",
  "SystemsTraceNumber": "000123",
  "ExpirationDate": "30/12",
  "POSEntryMode": "011",
  "POSConditionCode": "71",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "PrimaryAccountNumber": "4242424242424242",
  "RequestDataElements": {
    "InvoiceReferenceNumber": "ORD-1042",
    "ZipCode": "83702",
    "CVV2RequestValue": "11123 "
  }
}'
POST /hostapi/mailorder/

Mail order (CNP)

The CNP / e-commerce / MOTO equivalent of Sale. Same response shape – only the URL and POSConditionCode change. AVS happens off ZipCode + Address; the CVV2 result comes back in ResponseDataElements.CVV2ResultCode.

FieldTypeDescription
MessageTyperequiredstringAlways '200'.
ProcessingCoderequiredstring'000000'.
POSEntryModerequiredstring'012' – key-entered, cardholder not present.
POSConditionCoderequiredstringCNP / MOTO condition code (e.g. '08').
PrimaryAccountNumberrequiredstringCard number (PAN).
RequestDataElements.ElectronicCommerceIndicatorrequiredstringECI for the channel (e.g. '01', or '07' for a TLS-secured channel).
RequestDataElements.CardHolderFirstNameoptionalstringCardholder first name.
RequestDataElements.CardHolderLastNameoptionalstringCardholder last name.
RequestDataElements.ZipCodeoptionalstringBilling ZIP for AVS.
RequestDataElements.AddressoptionalstringBilling street for AVS.
RequestDataElements.CVV2RequestValueoptionalstring6-character CVV2 control field.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/mailorder/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "200",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000002500",
  "LocalTransactionTime": "094500",
  "LocalTransactionDate": "06/09",
  "SystemsTraceNumber": "000456",
  "ExpirationDate": "30/12",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "PrimaryAccountNumber": "4242424242424242",
  "RequestDataElements": {
    "ElectronicCommerceIndicator": "01",
    "CardHolderFirstName": "Jane",
    "CardHolderLastName": "Doe",
    "ZipCode": "83702",
    "Address": "412 W State St",
    "CVV2RequestValue": "11123 "
  }
}'
POST /hostapi/preauthorization/ → /completion/

Pre-auth + completion

Reserve funds at one amount, then capture at the final amount when you know it. Common for hospitality (hold $200, charge $187 on checkout), fuel (hold $125 at the pump, charge actual fill), or staged shipments.

POST/hostapi/preauthorization/Step 1 – reserve funds at the high-water amount. MTI 0100, ProcessingCode 100000.
POST/hostapi/completion/Step 2 – capture at the final amount after fulfillment. MTI 0220, ProcessingCode 110000; references the pre-auth via OriginalSystemTraceAuditNumber + AuthorizationIdResponse.

ProcessingCode differs per message family. It is not 000000 everywhere. A 0100 sent with 000000 answers 12 INVALID TRANSACTION; the identical packet with 100000 approves, and the completion class is 110000. See the hospitality flow for the full certified sequence.

The completion needs to reference the pre-auth’s STAN + auth code so the gateway can match.

FieldTypeDescription
MessageTyperequiredstring'0100' for the pre-auth; '0220' for the completion.
ProcessingCoderequiredstring'100000' on the pre-auth; '110000' on the completion.
TransactionAmountrequiredstringPre-auth: the high-water hold amount. Completion: the final captured amount. 12-digit cents.
AuthorizationIdResponserequiredstringAuth code from the pre-auth; required on the completion to match.
RequestDataElements.OriginalSystemTraceAuditNumberrequiredstringSTAN of the original pre-auth (completion only).
RequestDataElements.OriginalTransactionDaterequiredstringDate of the original pre-auth as slashed YY/MM/DD. A bare MMDD is rejected.
RequestDataElements.OriginalTimerequiredstringhhmmss of the original pre-auth, exactly as transmitted.
RequestDataElements.OriginalMessageTyperequiredstring3-digit MTI of the original ('100'). The 4-digit form '0100' is rejected.
RequestDataElements.OriginalAmountrequiredstringThe ORIGINAL authorized amount, 12-digit cents - not the amount being captured.
RequestDataElements.AuthorizedAmountrequiredstringSame value as OriginalAmount.

An approved completion response does not return AuthorizationIdResponse. The approval code appears only inside AlternteResponseText, so any later message needing an auth code must reference the ORIGINAL pre-auth’s code.

↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/completion/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "0220",
  "ProcessingCode": "110000",
  "TransactionAmount": "000000008500",
  "AuthorizationIdResponse": "234562",
  "RequestDataElements": {
    "OriginalSystemTraceAuditNumber": "000123",
    "OriginalTransactionDate": "25/06/09",
    "OriginalTime": "103200",
    "OriginalMessageType": "100",
    "OriginalProcessingCode": "100000",
    "OriginalAmount": "000000010000",
    "AuthorizedAmount": "000000010000"
  }
}'
POST /hostapi/onlinerefund/

Refund

Credit funds back to the cardholder. Same envelope as Sale – the only difference is ProcessingCode 200000 (credit rather than debit). MTI is still 200.

POST/hostapi/onlinerefund/Refund when the original transaction is in a different batch (the common case). MTI 200.
POST/hostapi/offlinerefund/Refund within the same batch as the original. MTI 220.
FieldTypeDescription
MessageTyperequiredstringStill '200' – same as a sale.
ProcessingCoderequiredstring'200000' – credit rather than debit.
TransactionAmountrequiredstringAmount to credit back, 12-digit cents.
PrimaryAccountNumberrequiredstringCard to refund.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/onlinerefund/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "200",
  "ProcessingCode": "200000",
  "TransactionAmount": "000000001500",
  "LocalTransactionTime": "151230",
  "LocalTransactionDate": "06/09",
  "SystemsTraceNumber": "000789",
  "ExpirationDate": "30/12",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "PrimaryAccountNumber": "4242424242424242"
}'
POST /hostapi/reversalfull/ · /reversalpartial/

Reversal (void)

Reverses a same-batch transaction: a sale, a mail order, an authorization (releases the hold), or a pre-authorization whose completion did not go through. Send it while the batch is open; after the batch closes, use Refund instead. A captured completion is not reversed by this message: the host approves it and keeps the capture in the batch. Refund a captured completion.

POST/hostapi/reversalfull/Full reversal. MTI 0400. TransactionAmount is the original amount, or the cumulative amount when reversing an incremented authorization.
POST/hostapi/reversalpartial/Partial reversal. MTI 0420. TransactionAmount is the amount being released; add RequestDataElements.AuthorizedAmount (the original amount) and the envelope ResponseCode "00" from the original response.

The host locates the original by RetrievalReferenceNumber, STAN, NetworkReferenceNumber and the Original* elements, and it needs PrimaryAccountNumber and ExpirationDate on the reversal. Without the card the response is 31 CALL HELP - NS. The envelope frame (STAN, LocalTransactionTime, LocalTransactionDate) is new; OriginalTime and OriginalTransactionDate are the original message’s own values.

FieldTypeDescription
MessageTyperequiredstring'0400' full reversal; '0420' partial reversal. Four digits.
ProcessingCoderequiredstring'000000'.
TransactionAmountrequiredstringAmount to reverse, 12-digit cents.
ResponseCodeoptionalstring0420 only: the original's response code ('00'). Do not send it on the 0400.
AuthorizationIdResponserequiredstringAuth code of the original transaction.
RetrievalReferenceNumberrequiredstringDE 37 of the original. The strongest match key.
PrimaryAccountNumber / ExpirationDaterequiredstringThe card, formatted as the original sent it. Use TokenizationElements.Token when the original carried a token.
RequestDataElements.ReasonCoderequiredstring'02' void/cancel; '01' timeout reversal.
RequestDataElements.OriginalSystemTraceAuditNumberrequiredstringSTAN of the original, verbatim.
RequestDataElements.OriginalMessageTyperequiredstringMTI of the original, four digits ('0200' sale, '0100' authorization).
RequestDataElements.OriginalProcessingCoderequiredstringProcessingCode of the original ('000000' sale, '100000' authorization).
RequestDataElements.OriginalAmountrequiredstringOriginal transaction amount, 12-digit cents.
RequestDataElements.OriginalTransactionDate / OriginalTimerequiredstring'YY/MM/DD' and 'HHMMSS' as the original was sent.
RequestDataElements.NetworkReferenceNumberoptionalstringFrom the original response, when present.
RequestDataElements.AuthorizedAmountoptionalstring0420 only: the original amount, 12-digit cents.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/reversalfull/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "0400",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000000201",
  "LocalTransactionTime": "114950",
  "LocalTransactionDate": "09/08",
  "SystemsTraceNumber": "590003",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "AcquirerId": "<ACQUIRER_ID>",
  "AuthorizationIdResponse": "<DE38 of the original>",
  "RetrievalReferenceNumber": "<DE37 of the original>",
  "PrimaryAccountNumber": "<PAN>",
  "ExpirationDate": "29/07",
  "RequestDataElements": {
    "InvoiceERCReferenceNumber": "<original STAN>",
    "BatchNumber": "00052",
    "ReasonCode": "02",
    "HardwareVendorIdentifier": "FISP",
    "SoftwareIdentifier": "0002",
    "CardType": "CR",
    "OriginalMessageType": "0200",
    "OriginalSystemTraceAuditNumber": "<original STAN>",
    "OriginalAmount": "000000000201",
    "OriginalProcessingCode": "000000",
    "OriginalTransactionDate": "26/09/08",
    "OriginalTime": "<DE12 of the original>",
    "NetworkReferenceNumber": "<from the original response>",
    "ResponseACI": "",
    "ValidationCode": ""
  }
}'
POST /hostapi/balanceinquiry/

Balance inquiry

Inquires the available balance on a card. No transaction is recorded – pure read.

FieldTypeDescription
MessageTyperequiredstring'100'.
ProcessingCoderequiredstring'310000' – balance inquiry.
TransactionAmountrequiredstringAll zeros – read-only, no amount is moved.
PrimaryAccountNumberrequiredstringCard to inquire.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/balanceinquiry/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "100",
  "ProcessingCode": "310000",
  "TransactionAmount": "000000000000",
  "PrimaryAccountNumber": "<CARD>",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>"
}'
POST /hostapi/cardverification/

Card verification

Probes the card without authorizing an amount. Use when you want to verify a card-on-file is still valid before billing – common for recurring subscriptions and stored-card vault enrollment.

FieldTypeDescription
MessageTyperequiredstring'0100'.
ProcessingCoderequiredstring'380000' – the card-verification family (38aa0x). '000000' answers 12 INVALID TRANSACTION.
TransactionAmountrequiredstringAll zeros – required to be zero for card verification. No hold is placed.
PrimaryAccountNumberrequiredstringCard to verify.
ExpirationDaterequiredstringCard expiry as YY/MM, with a slash.
RequestDataElements.ZipCodeoptionalstringBilling ZIP for AVS.
RequestDataElements.CVV2RequestValueoptionalstring6-character CVV2 control field.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/cardverification/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "0100",
  "ProcessingCode": "380000",
  "TransactionAmount": "000000000000",
  "PrimaryAccountNumber": "<CARD>",
  "ExpirationDate": "30/12",
  "RequestDataElements": {
    "ZipCode": "83702",
    "CVV2RequestValue": "11123 "
  }
}'
DE 61

Tokenization

The production tokenization path is a dedicated service, separate from this ISO-8583 gateway: a mutual-TLS API (tokenize / detokenize) that swaps a PAN for an opaque token and back. Tokenize the PAN once, store only the token, and at void / refund / recurring time detokenize just long enough to build the packet. It requires a client certificate (mTLS) and an IP allow-list; ask EPI to enable it.

The Token then rides TokenizationElements.Token (tag T03) on the gateway call in place of PrimaryAccountNumber – one of PAN / track / token / encrypted-PAN is required on every authorization, financial, and reversal message, and the token satisfies that.

Note – inline indicator (unverified). The reference guide also documents an inline option: TokenizationElements.TokenIndicator: "R"on the sale to have the host return a token in the response. EPI has not certified this against live Cygma (the spec marks the standalone tokenization transaction “reserved for future use”), so the dedicated service above is the path we use.

FieldTypeDescription
TokenizationElements.TokenoptionalstringToken (T03, 13–19 digits) standing in for the PAN; send in place of PrimaryAccountNumber on voids, refunds, and recurring charges.
TokenizationElements.TokenRequestorIdoptionalstringTRID (T07) – 11-digit token requestor identifier, echoed on token responses.
TokenizationElements.TokenIndicatoroptionalstringInline option (unverified): 'R' on a sale/auth request asks the host to return a token in the response. Prefer the dedicated tokenize service.
↑ Request – what you send
// Dedicated tokenize service (mutual-TLS) – swap PAN <-> token.
// POST https://cygtoken.cygma.com:9454/api/v1/tokenize
{ "pan": "4242424242424242", "create": true }
//   -> { "token": "545721XXXXXXXX14" }

// POST https://cygtoken.cygma.com:9454/api/v1/detokenize
{ "token": "545721XXXXXXXX14" }
//   -> { "pan": "4242424242424242" }

// Then on the gateway, send the Token (T03) in place of the PAN
// for a void / refund / recurring charge:
{
  "MessageType": "0200",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000002500",
  "TokenizationElements": { "Token": "545721XXXXXXXX14" },
  ...
}
Commercial card interchange

Level II qualification

Level II data qualifies a transaction for commercial-card interchange categories when the cardholder pays with a business / corporate / purchasing card.

Every Level II field is a named top-level key of RequestDataElements (DE 62). Each maps to an L-prefix tag in the host dictionary; conditions come from the host spec – M mandatory, C99 required for Level III qualification (see the chart below).

Minimum Level II set: SalesTaxAmount + SalesTaxCollectedIndicator + MerchantOrderCustomerReferenceNumber. The C99 rows become required when the transaction also carries Level III line items.

FieldTypeDescription
SalesTaxAmountrequiredL11 · MTotal state sales tax, 12-digit zero-padded cents ('000000000150' = $1.50).
SalesTaxCollectedIndicatorrequiredL12 · C99'0' = no tax information · '1' = tax amount provided · '2' = tax exempt / non-taxable. There is no separate TaxExemptIndicator field – exemption is this value.
MerchantOrderCustomerReferenceNumberrequiredL24Invoice / reference number (alphanumeric, max 17). REQUIRED whenever SalesTaxAmount (L11) is sent.
CustomerCodeoptionalL10 · OCode the cardholder supplies to the merchant – commonly the card's last 4. Alphanumeric, max 38. Visa / MC / Amex / Discover.
CommercialRequestIndicatoroptionalL01 · O'1' requests commercial-card qualification ('0' = no request). Visa.
OrderDateoptionalL21 · CDate the order was placed, YYMMDD.
DiscountAmountrequiredL14 · C99Total discount applied, 12-digit cents.
DiscountAmountCreditDebitIndicatorrequiredL26 · C99Credit/debit indicator for DiscountAmount.
FreightShippingAmountrequiredL15 · C99TOTAL freight / shipping and handling, 12-digit cents. Header-level – never per line item.
FreightShippingAmountCreditDebitIndicatorrequiredL27 · C99Credit/debit indicator for FreightShippingAmount.
DutyAmountrequiredL16 · C99Import/export duty amount, 12-digit cents.
DutyAmountCreditDebitIndicatorrequiredL25 · C99Credit/debit indicator for DutyAmount.
SalesTaxRateoptionalL09Sales tax rate (5 chars), optional.
DestinationCountryCodeoptionalL17Country where goods will be delivered ('USA').
ShipFromPostalCodeoptionalL18Postal code goods ship FROM (merchant), max 10.
DestinationPostalCodeoptionalL19Postal code goods ship TO (customer), max 10.
ElectronicCommerceIndicatoroptionalI02'01' for key-entered CNP – any other ECI on a CNP transaction downgrades the rate.

For key-entered card-present (POSEntryMode 011): also include ZipCode, Address, CVV2RequestValue.

For CNP / MOTO (POSEntryMode 012 / condition 08): include all the above PLUS ElectronicCommerceIndicator: "01".

↑ Request – what you send
// Level II header fields – every one of these is a
// top-level key of RequestDataElements (DE 62).
// Tag numbers refer to the L-tag dictionary below.

{
  "RequestDataElements": {
    "MarketSpecificDataRequest":             " ",
    "RequestedACI":                          "Y",
    "CommercialRequestIndicator":            "1",             // L01
    "CustomerCode":                          "4242",          // L10
    "SalesTaxAmount":                        "000000000150",  // L11 ($1.50)
    "SalesTaxCollectedIndicator":            "1",             // L12
    "MerchantOrderCustomerReferenceNumber":  "IN0191",        // L24
    "OrderDate":                             "260823",        // L21
    "DiscountAmount":                        "000000000000",  // L14
    "DiscountAmountCreditDebitIndicator":    "0",             // L26
    "FreightShippingAmount":                 "000000000000",  // L15
    "FreightShippingAmountCreditDebitIndicator": "0",         // L27
    "DutyAmount":                            "000000000000",  // L16
    "DutyAmountCreditDebitIndicator":        "0",             // L25
    "ElectronicCommerceIndicator":           "01"             // I02 (CNP)
  }
}
Line-item detail · commercial card qualification

Level III qualification

Level III adds line-item detail (quantity, unit cost, commodity code, unit of measure) for every product on the order, submitted alongside the Level II data.

Repeating line-item tags travel ONLY inside Level3Items1Level3Items15 (host tag L99, maximum 15 items). Each value is a packed TLV string: 3-char tag + 4-digit ASCII length + value, concatenated with no separators. Sending a repeating tag as its own top-level key is silently ignored by the host – it must be inside a Level3Items string to count.

Required per item (condition C99): L31 product code, L32 description, L34 unit of measure, L35 extended amount, L36 debit/credit indicator, L37 line discount, L40 unit cost. Required at header: the Level II C99 set above (tax indicator, discount / freight / duty amounts + indicators) plus SalesTaxAmount and MerchantOrderCustomerReferenceNumber.

A commodity code (L30) is needed to qualify – use the NIGP class/subclass list (e.g. 61500 office supplies, 20511 microcomputers). The full list is available as a spreadsheet: cygma-commodity-codes.xlsx (2,839 codes across 192 classes). Amounts inside items are 12-digit zero-padded cents; quantities are 9-digit.

FieldTypeDescription
Level3Items1 … Level3Items15requiredL99 · COne packed TLV string per line item (max 15). Tag order per the host sample: L30 L31 L32 L33 L34 L35 L36 L37 L38 L39 L40 [L41 L42 L43].

The complete per-tag reference – with types, widths, conditions, and card-brand mappings – is in the L-tag dictionary below.

↑ Request – what you send
// ONE line item = ONE Level3ItemsN key (tag L99).
// Up to 15 items: Level3Items1 .. Level3Items15.
// Submit ALONGSIDE the Level II header fields above.

{
  "RequestDataElements": {
    // ...all Level II header fields above...
    "Level3Items1": "L3000100000061500L310006WIDGETL320011BLUE WIDGETL330009000000002L340002EAL350012000000002000L360001DL370012000000000000L380001NL390001NL400012000000001000",
    "Level3Items2": "L3000100000020511L310008COMPUTERL320008COMPUTERL330009000000001L340002EAL350012000000005000L360001DL370012000000000000L380001NL390001NL400012000000005000"
  }
}
DE 62 · Commercial card Level II / III reference

L-tag dictionary & charts

Tag prefixes group the DE 62 / DE 63 private-use dictionary; conditions come from the Code/Meaning chart.

Tag prefix chart

FieldTypeDescription
AoptionalprefixCommon Data Elements (e.g. A01 InvoiceERCReferenceNumber).
IoptionalprefixElectronic Commerce (I02 ElectronicCommerceIndicator).
JoptionalprefixIndustry Specific Data – lodging, fleet, bill payment, EBT, telecom.
HoptionalprefixHotel/Lodging industry data. Only 4-char tag prefix – all others are 3.
LoptionalprefixCommercial Card Level II and Level III.
MoptionalprefixMiscellaneous.
PoptionalprefixNetwork Specific Fields.
RoptionalprefixResponse Fields.
SoptionalprefixReconciliation Totals (settlement).
ToptionalprefixToken Data (T03 Token, T07 TokenRequestorId).

Condition Code/Meaning chart

FieldTypeDescription
MoptionalconditionMandatory.
OoptionalconditionOptional.
CoptionalconditionConditional – required when its trigger condition applies.
C01optionalconditionPAN present in DE 2 when no track data is present (keyed entry).
C02optionalconditionExpiration date included when the card number was entered manually.
C03optionalconditionTrack I and/or II included when the card is read from the magnetic stripe.
C05optionalconditionPOS Entry Mode: first two digits '01' keyed / '02' card reader; last digit '1' PIN capable / '2' no PIN.
C06optionalconditionKeyed account number ⇒ DE 2 included; swiped ⇒ track data included.
C07optionalconditionEMV: terminal with ICC capability performing a chip transaction.
C99optionalconditionREQUIRED FOR LEVEL III QUALIFICATION. Header: L12 L14 L15 L16 L25 L26 L27. Per line item: L31 L32 L34 L35 L36 L37 L40.

L-tag dictionary – header fields (top-level RequestDataElements keys)

FieldTypeDescription
L01 CommercialRequestIndicatoroptionalan 1 · O'1' = request commercial card status, '0' = no request. Visa.
L02 CommercialCardResponseIndicatoroptionalan 1 · CResponse-side: whether the card is Business / Corporate / Purchase. Visa.
L09 SalesTaxRateoptionalan 5 · OSales tax rate.
L10 CustomerCodeoptionalan 38 · OCardholder-supplied code (commonly card last 4). All brands.
L11 SalesTaxAmountrequiredun 12 · MTotal state sales tax, zero-padded cents. The core Level II amount.
L12 SalesTaxCollectedIndicatorrequiredan 1 · C99'0' no tax info · '1' tax provided · '2' tax exempt / non-taxable.
L13 AltTaxAmountoptionalan 12 · OAlternate tax amount.
L14 DiscountAmountrequiredun 12 · C99Total discount applied. MC Common Data p0732.
L15 FreightShippingAmountrequiredun 12 · C99TOTAL freight/shipping. MC p0606.
L16 DutyAmountrequiredan 12 · C99Import/export duty total. MC p0607.
L17 DestinationCountryCodeoptionalan 3 · CDelivery country. Amex DF63/DF47, MC p0610.
L18 ShipFromPostalCodeoptionalan ..10 · CShip-from (merchant) postal code. MC p0613.
L19 DestinationPostalCodeoptionalan ..10 · CShip-to (customer) postal code. Amex DF63.
L20 UniqueVATInvoiceReferenceNumberoptionalan ..15 · CUnique VAT invoice reference.
L21 OrderDateoptionalun 6 · COrder date YYMMDD. MC P0614.
L24 MerchantOrderCustomerReferenceNumberrequiredan ..17Invoice/reference number. REQUIRED when L11 SalesTaxAmount is sent.
L25 DutyAmountCreditDebitIndicatorrequiredun 1 · C99D/C indicator for L16. MC Duty Amount Sign.
L26 DiscountAmountCreditDebitIndicatorrequiredan 1 · C99D/C indicator for L14.
L27 FreightShippingAmountCreditDebitIndicatorrequiredan 1 · C99D/C indicator for L15.
L44 ShipDateoptionalun 6 · ODate merchandise shipped. MasterCard.

L-tag dictionary – repeating line-item fields (inside Level3Items TLV only; ignored at header)

FieldTypeDescription
L30 ItemCommodityCoderequiredan 15 · OCommodity code of the goods (NIGP class/subclass – see the spreadsheet above). Listed O in the host dictionary; the card brands require it to qualify. Visa / MC / Discover. MC p0679.
L31 ProductCoderequiredan ..12 · C99Product code / SKU of the item. Amex DF47 s12, MC p0641.
L32 ItemDescriptionrequiredan ..26 · C99Description of the purchased item. MC p0642.
L33 ItemQuantityrequiredun 12 · ONumber of items purchased (9-digit zero-padded in practice). All brands. MC p0643.
L34 ItemUnitOfMeasurerequiredan ..12 · C99International trade unit code ('EA' each). MC p0645.
L35 ExtendedItemAmountrequiredun 12 · C99Line total (qty × unit cost − discount). MC p0647 s1.
L36 ExtendedAmountCreditDebitIndicatorrequiredan 1 · C99'D' debit (normal sale line) / 'C' credit. MC p0647 s3.
L37 DiscountAmountPerLineItemrequiredun 12 · C99Discount applied at line level. MC p0648 s2.
L38 ItemDiscountIndicatoroptionalan 1 · C'Y' line was discounted / 'N' not. MC p0648 s1.
L39 ZeroCostToCustomerIndicatoroptionala 1 · C'Y' item provided at no cost / 'N'. MC p0650.
L40 UnitCostrequiredun 12 · C99Unit price of the item. MC p0646.
L41 VATRateAppliedoptionalun 5 · OVAT rate applied to the line.
L42 VATTaxTypeoptionalan 4 · OType of value-added tax.
L43 VATTaxAmountoptionalun 12 · OVAT amount for the line.
L45 ShippingMethodoptionalan 2 · O0100/0200 only: '01' same day · '02' overnight · '03' priority 2-3d · '04' ground · '05' electronic · '06' ship-to-store.
L46-L49 ShipTo Name/Address/Phoneoptionalan · OAmex 0100/0200: ship-to first (15) / last (30) / address (50) / phone (10) – UPPER CASE, space-filled, no zero/virgule filler.
L65-L67 Promo Code/Start/EndoptionalODiscover promotional code (6) + start/end dates (8).

Required tags to build a Level II packet – all card brands

FieldTagValue
MarketSpecificDataRequestSingle space " "
RequestedACI"Y"
SalesTaxAmountL1112-digit cents
SalesTaxCollectedIndicatorL12"1" collected · "2" exempt · "0" none
MerchantOrderCustomerReferenceNumberL24Invoice/reference number (alnum ≤17)
ElectronicCommerceIndicatorI02"01" – CNP transactions only

Required tags to build a Level III packet – all card brands (in addition to the Level II set)

WhereTags
Header (top-level keys)L14 L26 · L15 L27 · L16 L25 (discount / freight / duty amounts + their D/C indicators – send zeros/"0" when not applicable)
Each Level3Items stringL30 L31 L32 L33 L34 L35 L36 L37 L40 (+ L38 L39 indicators; + L41 L42 L43 when VAT applies)

Network field mapping – where each tag lands per card brand

TagVisaMastercardDiscoverAmex
L10Line Item p0508
L11SDR 10
L14 / L26Common Data p0732
L15 / L27Common Data p0606
L16 / L25Common Data p0607
L17p0610MOTO SDRDF63 206-208
L18 / L19p0613Geographic SDRDF63 92-100
L30p0679
L31Line Item p0641SDRDF47 s12 / s3
L32Line Item p0642SDR
L33Line Item p0643
L34Line Item p0645SDR
L35 / L36Line Item p0647
L37 / L38Line Item p0648SDR
L39Line Item p0650
L40Line Item p0646SDR
L44
L45–L49DF47 s10 / DF63
L65–L67✓ promo

Tags L03–L08 and L74/L75/L79–L98 do not exist. There is no “order amount” or “line sequence” tag – ordering is implied by Level3Items1…15. Tax exemption is signaled via L12 = "2"; the legacy TaxExemptIndicator key is accepted by the host but is not part of the dictionary. L50–L64 carry VAT sets 2–6; L68–L73 and L76–L78 are Discover/healthcare industry extras.

Do NOT send a CardScheme key inside RequestDataElements – the host rejects the transaction as code 30 format error.

↑ Request – what you send
// Level II + III sale. This is the packet exactly as sent,
// with credentials and card data as placeholders. The shape is
// IDENTICAL for all four brands – the only per-brand differences
// are that CVV2RequestValue carries a 4-digit CID on Amex
// ("111234" vs "11123 "), and CustomerCode is the card's last 4.

{
  "MessageType": "0200",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000001045",
  "LocalTransactionTime": "174911",
  "LocalTransactionDate": "08/23",
  "SystemsTraceNumber": "751001",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "AcquirerId": "<ACQUIRER_ID>",
  "PrimaryAccountNumber": "<PAN>",
  "ExpirationDate": "28/03",
  "RequestDataElements": {
    "BatchNumber": "00022",
    "CardType": "CR",
    "HardwareVendorIdentifier": "FISP",
    "SoftwareIdentifier": "0002",
    "MarketSpecificDataRequest": " ",
    "RequestedACI": "Y",
    "ZipCode": "11933",
    "Address": "1161",
    "CVV2RequestValue": "11123 ",
    "SalesTaxAmount": "000000000095",
    "SalesTaxCollectedIndicator": "1",
    "TaxExemptIndicator": "0",
    "Level3Items1": "L3000100000061500L310006WIDGETL320006WIDGETL330009000000001L340002EAL350012000000000950L360001DL370012000000000050L380001YL390001NL400012000000001000",
    "CommercialRequestIndicator": "1",
    "CustomerCode": "1205",
    "DiscountAmount": "000000000050",
    "DiscountAmountCreditDebitIndicator": "C",
    "FreightShippingAmount": "000000000000",
    "FreightShippingAmountCreditDebitIndicator": "0",
    "DutyAmount": "000000000000",
    "DutyAmountCreditDebitIndicator": "0",
    "OrderDate": "260823",
    "DestinationCountryCode": "USA",
    "DestinationPostalCode": "11933",
    "MerchantOrderCustomerReferenceNumber": "INV-0003",
    "ElectronicCommerceIndicator": "01"
  }
}

// Decoded Level3Items1 (one line: WIDGET, qty 1, $10.00 unit,
// 50c line discount, commodity 61500):
//   L30 0010 0000061500   commodity code
//   L31 0006 WIDGET       product code
//   L32 0006 WIDGET       description
//   L33 0009 000000001    quantity
//   L34 0002 EA           unit of measure
//   L35 0012 000000000950 extended amount
//   L36 0001 D            debit line
//   L37 0012 000000000050 line discount
//   L38 0001 Y            line was discounted
//   L39 0001 N            not zero-cost
//   L40 0012 000000001000 unit cost
J26 IndustryIndicator · enhanced data by industry

Industry addenda

An industry addendum describes WHAT was bought, in the vocabulary the card networks defined for that trade. A hotel sends arrival and departure dates and a room rate; a fuel pump sends gallons, price per gallon and an odometer reading; an airline sends a ticket number and an itinerary. The authorization approves with or without it – what the data buys is the industry interchange rate and a chargeback defence, since the issuer can see the stay or the trip rather than a bare amount.

Two rules cover the whole mechanism. First, set IndustryIndicator (tag J26) to the industry’s number – that is what tells the host how to read the rest. Second, send the industry’s own tags as ordinary keys inside RequestDataElements. There is no wrapper object and no repeating container, unlike Level III.

Send the indicator only alongside real data. Announcing an industry and then describing nothing is worse than sending neither: the host has been told to expect a hotel and finds no folio. Omit IndustryIndicator when you have no addendum fields to go with it.

Widths and implied decimals are not uniform, and this is what catches integrators.Every amount is a zero-filled integer with no decimal point, but the scale differs BY FIELD rather than by type – a single fuel message carries three of them. Unit price and the gross and net fuel prices take four implied decimals in a 12-digit slot; the sale amount and the non-fuel prices take two in the same width; fuel quantity takes threein a 6-digit slot. A wrong scale is not rejected – it is accepted as a different number. $38.39 at four decimals is 000000383900, and sending 000000003839 books 38 cents. Read the scale off the per-field tables below rather than inferring it.

Fixed-width alphanumeric codes are space-padded on the right. Level III’s AN tags are variable-length and never padded, but a fixed-width code slot is different: Visa’s own worked examples show Expanded Fuel Type as 19   and FC  – a two-character code left-justified in four. The tables mark those fields space-padded.

Not every brand carries every industry. Where a field or a whole profile is defined by only some networks, the tables say so. Sending a profile to a brand that does not define it earns nothing and risks a format error, so gate on the brand rather than sending it universally.

Industries carried
FieldTypeDescription
FUELoptionalJ26 = 8FuelPump and product detail for a fuel purchase. Mastercard and Discover carry different subsets; Visa fuel rides the Fleet profile. 14 fields.
AIRLINEoptionalJ26 = 1AirlineTicket, passenger and itinerary detail. Captures the ticket header and the first air segment. 29 fields.
HEALTHCAREoptionalJ26 = 18HealthcareProvider and payer identifiers. Per-line healthcare eligibility is set on the Level III grid. 3 fields.
CRUISEoptionalJ26 = 19CruiseSailing, itinerary and the air leg to the port, plus agency identifiers. 19 fields.
RAILoptionalJ26 = 15RailTicket, journey and service detail for rail travel. 19 fields.
ELECTRIC_FUELoptionalJ26 = 26Electric vehicle chargingEV charging session — connector, energy, timings and station capacity. Required for MCC 5552 in Europe now and in the US by 2030. 15 fields.
TRAVELoptionalJ26 = 17Travel agencyAgency identifiers and the service fee charged on a travel booking. 7 fields.
INSURANCEoptionalJ26 = 22InsurancePolicy, insured party and premium detail. 7 fields.
TELEPHONEoptionalJ26 = 14TelephoneOriginating and destination numbers for a call-based charge. 3 fields.
TICKET_ENTERTAINMENToptionalJ26 = 16Ticketing / entertainmentEvent, venue and ticket detail. 9 fields.
VISA_TRANSPORT_ANCILLARYoptionalJ26 = 23Transport ancillaryBaggage, seating or other purchases attached to a travel document rather than the ticket. 4 fields.
HOTELoptionalJ26 = 4Hotel / lodgingLodging enhanced data - the stay, the room, and itemised incidentals. Carried on all brands. 27 fields.
AUTO_RENTALoptionalJ26 = 6Auto rentalVehicle rental enhanced data - agreement, pickup and return detail, distance and adjustments. 21 fields.
FLEEToptionalJ26 = 25FleetVisa Fleet enhanced data — fuel type, quantity, pricing and the driver/vehicle prompts a fleet card asks for. 17 fields.
FuelJ26 = 814 fields · MC · DISC

Pump and product detail for a fuel purchase. Mastercard and Discover carry different subsets; Visa fuel rides the Fleet profile.

Consistency: FuelQuantity × FuelUnitPrice must equal the amount authorized and FuelSaleAmount. Solve to the field’s own precision — the rounded product will not always reproduce the amount to the cent, and the amount is what is charged.

Site
FieldTypeDescription
CompanyBrandNameauto-derivedB62 · an4Brand — Brand at the pump, 4 characters (e.g. SHEL). Pre-filled from the merchant name.
PurchaseTimeauto-derivedB63 · n4 · HHMMPurchase time — Local time at the pump, HH:MM. MC only.
FuelServiceTypeauto-derivedB64 · an1 · enumService type Values: S, F, H. MC only.
Product
FieldTypeDescription
FuelCoderequired to qualifyB69 · an2 · enumFuel code — Visa Fuel Type Code. 121 defined values. DISC only.
FuelUnitPricerequired to qualifyB71 · n12 · 4dp impliedPrice per gallon — Dollars per gallon, e.g. 3.499. MC only.
FuelQuantityrequired to qualifyB72 · n6 · 3dp impliedQuantity (gallons) — Gallons dispensed, e.g. 12.153. MC only.
FuelSaleAmountauto-derivedB73 · n12 · 2dp impliedFuel sale amount — The fuel portion of the sale. MC only.
Tax
FieldTypeDescription
TotalTaxAmountauto-derivedB65 · n12 · 2dp impliedTotal tax
TotalTaxCollectIndicatorauto-derivedB66 · an1 · enumTax collected Values: Y, N. MC only.
StateSaleTaxAmountauto-derivedB67 · n12 · 2dp impliedState sales tax — Pre-filled from the merchant's default tax rate in Settings. DISC only.
StateSaleTaxIdrequired to qualifyB68 · an1State tax ID — One character. DISC only.
TaxExemptNumberrequired to qualifyB70 · n12Tax exempt number — Digits only, up to 12. DISC only.
Vehicle
FieldTypeDescription
FleetOdometerReadingrequired to qualifyH165 · n7Odometer — Whole miles. Digits only - no commas, no decimals.
Tax
FieldTypeDescription
ExemptIndicatorrequired to qualify— · an1Exempt indicator
AirlineJ26 = 129 fields · All brands

Ticket, passenger and itinerary detail. Captures the ticket header and the first air segment.

Ticket
FieldTypeDescription
AirlineTicketNumberrequired to qualifyC02 · an15Ticket number
AirlinePassengerNamerequired to qualifyC10 · an25Passenger name
AirlineTransactionTyperequired to qualifyC01 · an2Transaction type
AirlineDocumentTyperequired to qualifyC03 · an2Document type
AirlineTicketIssueDaterequired to qualifyC08 · n8 · YYYYMMDDIssue date
AirlineTicketIssueCityrequired to qualifyC07 · an18Issue city
AirlineTicketingCarriernamerequired to qualifyC06 · an25Ticketing carrier
AirlineIATANumericCoderequired to qualifyC05 · n8IATA code
AirlineElectronicTicketIndicatorauto-derivedC14 · an1 · enumElectronic ticket Values: E, P.
AirlineRestrictedTicketIndicatorrequired to qualifyC75 · an1 · enumRestricted ticket Values: N, R.
AirlineNumberinPartyauto-derivedC09 · n3Passengers
AirlineTotalFareauto-derivedC78 · n12 · 2dp impliedTotal fare
Itinerary
FieldTypeDescription
AirlineTotalNumberAirSegmentsauto-derivedC15 · n2Air segments — Total legs on the ticket. Only the first is captured here.
AirlineDepartureLocationCodeSegmentrequired to qualifyC18 · an5From (airport)
AirlineArrivalLocationCodeSegmentrequired to qualifyC20 · an5To (airport)
AirlineDepartureDateSegmentrequired to qualifyC19 · n8 · YYYYMMDDDeparture date
AirlineDepartureTimerequired to qualifyC79 · n4 · HHMMDeparture time — HH:MM
AirlineArrivalTimerequired to qualifyC80 · n4 · HHMMArrival time — HH:MM
AirlineSegmentCarrierCoderequired to qualifyC21 · an4Carrier
AirlineFlightNumberSegmentrequired to qualifyC24 · an6Flight number
AirlineClassServiceCodeSegmentrequired to qualifyC23 · an3Class of service
AirlineSegmentFareBasisrequired to qualifyC22 · an15Fare basis
AirlineSegmentFarerequired to qualifyC25 · n12 · 2dp impliedSegment fare
AirlineStopOverIndicatorrequired to qualifyC17 · an1 · enumStopover Values: O, X.
Agency
FieldTypeDescription
AirlineTravelAgencyCoderequired to qualifyC73 · an8Agency code
AirlineTravelAgencyNamerequired to qualifyC74 · an25Agency name
AirlineCustomerCoderequired to qualifyC71 · an17Customer code
AirlineTicketChangeIndicatorrequired to qualifyC77 · an1Ticket change
AirlineCreditReasonIndicatorrequired to qualifyC76 · an1Credit reason
HealthcareJ26 = 183 fields · All brands

Provider and payer identifiers. Per-line healthcare eligibility is set on the Level III grid.

Provider
FieldTypeDescription
VisaHCProviderIDrequired to qualifyP27 · an15Provider ID — Visa healthcare provider identifier. VISA only.
VisaServiceTypeCoderequired to qualifyP28 · an4Service type VISA only.
HealthcarePayerIDrequired to qualifyP31 · an15Payer ID
CruiseJ26 = 1919 fields · All brands

Sailing, itinerary and the air leg to the port, plus agency identifiers.

Booking
FieldTypeDescription
CruisePassengerNamerequired to qualifyC38 · an25Passenger name
CruiseTravelTicketNumberrequired to qualifyC39 · an15Ticket number
CruiseNamerequired to qualifyC50 · an25Cruise / ship name
CruiseDepartureDaterequired to qualifyC46 · n8 · YYYYMMDDDeparture date
CruiseReturnDaterequired to qualifyC47 · n8 · YYYYMMDDReturn date
CruiseNumberOfDaysrequired to qualifyC49 · n3Nights
CruiseTotalCostauto-derivedC48 · n12 · 2dp impliedTotal cost
CruiseClassCoderequired to qualifyC45 · an3Class
CruiseTravelPackageIndicatorrequired to qualifyC37 · an1 · enumTravel package Values: Y, N.
Itinerary
FieldTypeDescription
CruiseDestinationCoderequired to qualifyC41 · an5Destination
CruiseCityNamerequired to qualifyC53 · an18City
CruiseRegionCoderequired to qualifyC51 · an3Region
CruiseCountryCoderequired to qualifyC52 · an3Country
Air
FieldTypeDescription
CruiseDepartureAirportrequired to qualifyC42 · an5Departure airport
CruiseAirCarrierCoderequired to qualifyC43 · an4Air carrier
CruiseFlightNumberrequired to qualifyC44 · an6Flight number
CruiseDepartDaterequired to qualifyC40 · n8 · YYYYMMDDFlight date
Agency
FieldTypeDescription
CruiseIATACarrierCoderequired to qualifyC35 · an4IATA carrier
CruiseIATAAgencyNumberrequired to qualifyC36 · an8IATA agency number
RailJ26 = 1519 fields · All brands

Ticket, journey and service detail for rail travel.

Ticket
FieldTypeDescription
RailTransactionTyperequired to qualifyC26 · an2Transaction type
RailTicketNumberrequired to qualifyC27 · an15Ticket number
RailPassengerNamerequired to qualifyC28 · an25Passenger name
RailCarrierCoderequired to qualifyC29 · an4Carrier
RailTicketIssuerNamerequired to qualifyC30 · an25Issuer name
RailTicketIssuerCityrequired to qualifyC31 · an18Issuer city
Journey
FieldTypeDescription
RailLineItemSegmentDepartureLocationrequired to qualifyC32 · an5From
RailLineItemSegmentArrivalLocationrequired to qualifyC34 · an5To
RailLineItemSegmentDepartureDaterequired to qualifyC33 · n8 · YYYYMMDDDeparture date
RailClassrequired to qualifyC60 · an3Class
RailNumberOfAdultsauto-derivedC58 · n3Adults
RailNumberOfChildrenrequired to qualifyC59 · n3Children
Service
FieldTypeDescription
RailTravellerNamerequired to qualifyC54 · an25Traveller name
RailTicketNumrequired to qualifyC55 · an15Service ticket number
RailServiceTyperequired to qualifyC56 · an3Service type
RailServiceNaturerequired to qualifyC57 · an3Service nature
RailServiceAmountrequired to qualifyC61 · n12 · 2dp impliedService amount
RailServiceAmountSignrequired to qualifyC62 · an1 · enumAmount sign Values: D, C.
RailProcedureIdrequired to qualifyC63 · an8Procedure ID
Electric vehicle chargingJ26 = 2615 fields · All brands

EV charging session — connector, energy, timings and station capacity. Required for MCC 5552 in Europe now and in the US by 2030.

Consistency: Productquantity × FleetUnitPrice must equal the amount authorized and TotalAmountIncludingTax. Solve to the field’s own precision — the rounded product will not always reproduce the amount to the cent, and the amount is what is charged.

Session
FieldTypeDescription
ConnectorTyperequired to qualifyS30 · an3 · enumConnector type 9 defined values.
UnitOfMeasureauto-derivedS37 · an1 · enumUnit of measure — Electric sessions bill by kWh or by minute. Values: W, C.
Productquantityrequired to qualifyS42 · n12 · 4dp impliedQuantity (kWh)
FleetUnitPricerequired to qualifyS39 · n12 · 4dp impliedPrice per kWh
TotalAmountIncludingTaxauto-derivedS48 · n12 · 2dp impliedTotal including tax
StartTimeChargerequired to qualifyS46 · n4 · HHMMCharge start — HH:MM
FinishTimeChargerequired to qualifyS47 · n4 · HHMMCharge finish — HH:MM
TotalChargingTimerequired to qualifyS45 · n6Charging time (min)
TotalTimePluggedinrequired to qualifyS44 · n6Plugged in (min) — Can exceed charging time - idle minutes are often billed separately.
Station
FieldTypeDescription
MaxPowerDispensedrequired to qualifyS31 · n6Max power dispensed (kW)
CharginPowerCapacityrequired to qualifyS36 · n6Station capacity (kW) — May exceed max dispensed when the site manages power.
ChargingReasonCoderequired to qualifyS35 · an3 · enumCharging reason — Only when the session ended abnormally. 10 defined values.
Vehicle
FieldTypeDescription
EstMilesAddedrequired to qualifyS34 · n6Est. miles added
EstVehicleMilesAvailablerequired to qualifyS32 · n6Est. range on leaving
CarbonFootprintrequired to qualifyS33 · n12Carbon avoided (g CO2e)
Travel agencyJ26 = 177 fields · All brands

Agency identifiers and the service fee charged on a travel booking.

Agency
FieldTypeDescription
TravelAgencyCoderequired to qualifyH101 · an8Agency code
TravelAgencyNameauto-derivedH102 · an25Agency name
TravelAgencySeqNumberrequired to qualifyH085 · an8Sequence number
Fee
FieldTypeDescription
TravelAgencyFeeAmountrequired to qualifyH086 · n12 · 2dp impliedAgency fee
TravelAgencyFeeAmountSignauto-derivedH087 · an1 · enumFee sign Values: D, C.
TravelAgencyFeeAmountRaterequired to qualifyH088 · n6 · 2dp impliedFee rate (%)
TravelAgencyFeeDescriptionauto-derivedH089 · an25Fee description
InsuranceJ26 = 227 fields · All brands

Policy, insured party and premium detail.

Policy
FieldTypeDescription
InsurancePolicyNumberrequired to qualifyH148 · an25Policy number
AdditionalPolicyNumberrequired to qualifyH152 · an25Additional policy number
TypeOfPolicyrequired to qualifyH153 · an25Policy type
NameOfInsuredrequired to qualifyH154 · an30Name of insured
Premium
FieldTypeDescription
InsurancePremiumFrequencyrequired to qualifyH151 · an12 · enumPremium frequency Values: Monthly, Quarterly, Annual, Single.
InsuranceAmountauto-derivedH077 · n12 · 2dp impliedPremium amount
InsuranceIndicatorauto-derivedH131 · an1 · enumInsurance indicator Values: Y, N.
TelephoneJ26 = 143 fields · All brands

Originating and destination numbers for a call-based charge.

Call
FieldTypeDescription
CallFromPhoneNumberrequired to qualifyJ73 · n15 · digits onlyCall from
CallToPhoneNumberrequired to qualifyJ77 · n15 · digits onlyCall to
PhoneCardIdrequired to qualifyJ78 · an20Phone card ID
Ticketing / entertainmentJ26 = 169 fields · All brands

Event, venue and ticket detail.

Event
FieldTypeDescription
EventNameauto-derivedJ60 · an25Event name
EventDaterequired to qualifyJ61 · n8 · YYYYMMDDEvent date
EventLocrequired to qualifyJ64 · an25Venue
EventRegCoderequired to qualifyJ65 · an3Region
EventCntryCoderequired to qualifyJ66 · an3Country
Tickets
FieldTypeDescription
EventTktQtyauto-derivedJ63 · n4Tickets
EventIndTktPricerequired to qualifyJ62 · n12 · 2dp impliedPrice per ticket
TicketTyperequired to qualifyC86 · an4Ticket type
TicketIssuerAddressrequired to qualifyC83 · an25Issuer address
Transport ancillaryJ26 = 234 fields · All brands

Baggage, seating or other purchases attached to a travel document rather than the ticket.

Document
FieldTypeDescription
AncillaryTicketDocumentNorequired to qualifyC65 · an15Ticket document number
AncillaryAdditionalDocumentNorequired to qualifyC69 · an15Additional document number
AncillaryPassengerNamerequired to qualifyC68 · an25Passenger name
AncillaryCreditReasonIndicatorrequired to qualifyC70 · an1Credit reason
Hotel / lodgingJ26 = 427 fields · All brands

Lodging enhanced data - the stay, the room, and itemised incidentals. Carried on all brands.

Stay
FieldTypeDescription
HotelArrivalDaterequired to qualifyH016 · n8 · YYYYMMDDArrival date
HotelDepartureDaterequired to qualifyH017 · n8 · YYYYMMDDDeparture date
HotelFolioNumberrequired to qualifyH018 · an12Folio number
HotelRoomRaterequired to qualifyH021 · n12 · 2dp impliedRoom rate (nightly)
HotelRoomTaxrequired to qualifyH022 · n12 · 2dp impliedRoom tax
HotelNumberOfRoomsBookedauto-derivedH008 · n3Rooms booked
HotelNumberOfAdultsrequired to qualifyH009 · n3Adults
HotelNoShowIndicatorauto-derivedH011 · an1 · enumNo-show Values: N, Y.
Room
FieldTypeDescription
HotelRoomTyperequired to qualifyH006 · an12Room type
HotelBedTyperequired to qualifyH005 · an12Bed type
HotelRoomLocationrequired to qualifyH004 · an12Room location
HotelSmokingPreferencerequired to qualifyH007 · an1 · enumSmoking Values: N, S.
HotelRateTyperequired to qualifyH012 · an12Rate type
HotelProgramCoderequired to qualifyH023 · an12Program code
HotelPromotionalCoderequired to qualifyH001 · an12Promotional code
HotelCorporateClientCoderequired to qualifyH003 · an12Corporate client code
Incidentals
FieldTypeDescription
HotelPhoneChargesrequired to qualifyH024 · n12 · 2dp impliedPhone
HotelRestaurantRoomServiceChargesrequired to qualifyH025 · n12 · 2dp impliedRestaurant / room service
HotelMiniBarChargesrequired to qualifyH026 · n12 · 2dp impliedMini bar
HotelLaundryChargesrequired to qualifyH027 · n12 · 2dp impliedLaundry
HotelGiftShopChargesrequired to qualifyH030 · n12 · 2dp impliedGift shop
HotelMovieChargesrequired to qualifyH032 · n12 · 2dp impliedMovies
HotelHealthClubChargesrequired to qualifyH033 · n12 · 2dp impliedHealth club
HotelValetParkingChargesrequired to qualifyH034 · n12 · 2dp impliedValet parking
HotelCashDisbursementChargesrequired to qualifyH035 · n12 · 2dp impliedCash disbursement
HotelOtherChargesrequired to qualifyH028 · n12 · 2dp impliedOther
HotelAdjustmentAmountrequired to qualifyH020 · n12 · 2dp impliedAdjustment
Auto rentalJ26 = 621 fields · All brands

Vehicle rental enhanced data - agreement, pickup and return detail, distance and adjustments.

Agreement
FieldTypeDescription
RentalAgreementNumberrequired to qualifyB01 · an25Agreement number — Rental agreement number signed by the cardholder.
RentalRateIndicatorrequired to qualifyB38 · an1 · enumRate type Values: D, W, M.
RentalRaterequired to qualifyB39 · n12 · 2dp impliedRate
RentalVehicleClassIDrequired to qualifyB14 · an4Vehicle class
RentalDriverTaxNumberrequired to qualifyB22 · an20Driver tax number
Pickup
FieldTypeDescription
RentalPickupDaterequired to qualifyB06 · n8 · YYYYMMDDPickup date
RentalPickupTimerequired to qualifyB07 · n4 · HHMMPickup time — HH:MM
RentalPickupLocationrequired to qualifyB02 · an26Location
RentalPickupCityNamerequired to qualifyB03 · an18City
RentalPickupRegionCoderequired to qualifyB04 · an3State / region
RentalPickupCountryCoderequired to qualifyB05 · an3Country
Return
FieldTypeDescription
RentalReturnDaterequired to qualifyB11 · n8 · YYYYMMDDReturn date
RentalReturnTimerequired to qualifyB12 · n4 · HHMMReturn time — HH:MM
RentalDropofflocationrequired to qualifyB19 · an26Drop-off location
RentalReturnCityNamerequired to qualifyB08 · an25City
RentalReturnRegionCoderequired to qualifyB09 · an3State / region
RentalReturnCountryCoderequired to qualifyB10 · an3Country
RentalDistancerequired to qualifyB15 · n5Distance travelled — Whole units.
RentalDistanceUnitofMeasureauto-derivedB16 · an1 · enumDistance unit Values: M, K.
RentalAdjustmentIndicatorrequired to qualifyB17 · an1Adjustment type
RentalAdjustmentAmountrequired to qualifyB18 · n12 · 2dp impliedAdjustment amount
FleetJ26 = 2517 fields · VISA

Visa Fleet enhanced data — fuel type, quantity, pricing and the driver/vehicle prompts a fleet card asks for.

Consistency: Productquantity × FleetUnitPrice must equal the amount authorized and GrossFuelPrice. Solve to the field’s own precision — the rounded product will not always reproduce the amount to the cent, and the amount is what is charged.

Purchase
FieldTypeDescription
BusinessApplicationIdentifierauto-derivedP25 · an2Business application — Fleet business application identifier. F1 per Cygma's Visa Fleet sample.
Product
FieldTypeDescription
FleetFuelTypeoptionalH157 · an2Fuel type (2-char) — Two-character fuel type, e.g. GA. Sent alongside the expanded fuel type. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only.
Purchase
FieldTypeDescription
TypeOfPurchaserequired to qualifyS51 · an1 · enumType of purchase — MANDATORY on fleet. Drives which of the fields below Visa requires. Values: 1, 2, 3, 4.
VisaExpandFuelTypeoptionalS25 · an4 · enumFuel type — Visa Fuel Type Code. Required when type of purchase is 1, 3 or 4; blank when 2. 121 defined values. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - not sent on a non-fuel purchase.
ServiceTypeoptionalS52 · an1 · enumService type Values: S, F, H. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - not sent on a non-fuel purchase.
UnitOfMeasureoptionalS37 · an1 · enumUnit of measure 7 defined values. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - not sent on a non-fuel purchase.
ProductquantityoptionalS42 · n12 · 4dp impliedQuantity (gallons) — Gallons dispensed, e.g. 12.153. Four implied decimals. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - Visa requires 0 here on a non-fuel purchase.
FleetUnitPriceoptionalS39 · n12 · 4dp impliedPrice per gallon — Dollars per gallon, e.g. 3.499. Four implied decimals. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - Visa requires 0 here on a non-fuel purchase.
GrossFuelPriceoptionalS55 · n12 · 4dp impliedGross fuel price — Must equal quantity x unit cost, INCLUSIVE of taxes. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - Visa requires 0 here on a non-fuel purchase.
FleetNetFuelPriceoptionalH162 · n12 · 4dp impliedNet fuel price — Optional. Quantity x cost EXCLUSIVE of taxes. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only.
Non-fuel
FieldTypeDescription
FleetGrossNonFuelPriceoptionalH163 · n12 · 2dp impliedGross non-fuel price — Required when type of purchase is 2 or 3. Sum of the line items, inclusive of taxes. Applies only when TypeOfPurchase is “2” or “3”. Non-fuel only - Visa requires 0 here on a fuel-only purchase.
FleetNetNonFuelPriceoptionalH164 · n12 · 2dp impliedNet non-fuel price — Optional, exclusive of taxes. Applies only when TypeOfPurchase is “2” or “3”. Non-fuel only.
Vehicle
FieldTypeDescription
FleetOdometerReadingrequired to qualifyH165 · n7Odometer — Whole miles. Digits only - no commas, no decimals.
VisaFleetEmpNoauto-derivedS26 · an12Employee number — When the card prompts for it. Defaults to 1.
VisaFleetTrlrNorequired to qualifyS27 · an16Trailer number — When the card prompts for it.
VisaFleetAddProptData1required to qualifyS28 · an20Prompted data 1
VisaFleetAddProptData2required to qualifyS29 · an20Prompted data 2

The J26 dictionary also enumerates Visa Limited Data (7), Mail order / telephone order (9), Retail (10), Temporary services (24). Those are classifications rather than data sets – they carry no addendum fields, so there is nothing to send beyond the indicator itself.

↑ Request – what you send
// Hotel folio on a sale. The addendum tags are ordinary
// RequestDataElements keys - there is no wrapper object.

{
  "MessageType": "0200",
  "ProcessingCode": "000000",
  "RequestDataElements": {
    "IndustryIndicator": "4",
    "HotelArrivalDate": "20260901",
    "HotelDepartureDate": "20260904",
    "HotelFolioNumber": "884120",
    "HotelRoomRate": "000000018900",
    "HotelRoomTax": "000000002268",
    "HotelNumberOfRoomsBooked": "001",
    "HotelNumberOfAdults": "002",
    "HotelNoShowIndicator": "N",
    "HotelRestaurantRoomServiceCharges": "000000006420",
    "HotelMiniBarCharges": "000000001800"
  }
}
J26 = 8 / 25 · Visa Fleet 2.0 product codes

Fuel + fleet detail

These are two profiles split by brand, not by trade. Fuel (J26 = 8) is the Mastercard and Discover profile; fleet (J26 = 25) is the Visa one. There is no separate Visa fuel profile – Visa fuel data rides Fleet, which is why a fuel sale on a Visa card sends J26 = 25 and the S-tags rather than the B-tags. Send the profile that matches the card in hand; the tables mark which brands carry each one.

Both describe the same dispense – product, quantity, unit price, service type, odometer. Fleet adds the identifiers a fleet card exists to capture: employee number, trailer number, and the two free-form prompted-data fields the pump collects.

Product codes come from the Visa Fleet 2.0 tables, not from the NIGP commodity list. These are two different catalogues and they are not interchangeable. Fuel products (01 unleaded, 02 mid-grade, 19 diesel, and so on) and non-fuel products (B9, 45car wash, and so on) are also separate namespaces from each other – the same two characters mean different things in each table, so a code is only meaningful alongside the field it was sent in.

Type of purchase (S51) gates the rest of the fleet block. A non-fuel-only purchase has no fuel type, no service type and no unit of measure; sending them anyway describes a dispense that did not happen. The conditional fields are marked in the tables above with the value that enables them.

Unit of measure follows the market. US fuel is sold in gallons (G); litres (L) apply where the pump does. It follows from the fuel type rather than being an independent choice.

Odometer is a whole number of miles or kilometres– no separators and no decimal. It is a reading, not a measurement, and the host reads it as digits.

The full Visa product tables are published as a spreadsheet: visa-fleet-product-codes.xlsx – 133 fuel codes and 195 non-fuel codes, each with its Conexxus equivalent and its unit of measure.

↑ Request – what you send
// Fleet (J26 = 25) - VISA. This is also where Visa
// fuel data goes; there is no separate Visa fuel profile.
//
// 12.5000 gal x $3.0712 = $38.39
// VisaExpandFuelType is 2 chars in a 4-char slot, SPACE-
// padded on the right - Visa's own examples show "19bb".

{
  "RequestDataElements": {
    "IndustryIndicator": "25",
    "TypeOfPurchase": "1",
    "VisaExpandFuelType": "19  ",
    "ServiceType": "S",
    "UnitOfMeasure": "G",
    "Quantity": "000000125000",
    "PerUnitCost": "000000030712",
    "GrossFuelPrice": "000000383900",
    "FleetNetFuelPrice": "000000383900",
    "FleetOdometerReading": "0084231",
    "VisaFleetEmpNo": "1",
    "VisaFleetTrlrNo": "TRL-221"
  }
}
DE 54 AdditionalAmounts

Surcharge / Tip / Cashback

Surcharge, tip, and cashback amounts ride in the AdditionalAmounts field (DE 54). Two rules:

  • ProcessingCode stays "000000" for surcharge and tip. ONLY a cash-back request flips the first two digits to "09" – sending 090000 on a credit surcharge sale is a format-error decline (code 30).
  • TransactionAmount is the total – base + surcharge + tip + cashback.
FieldTypeDescription
ProcessingCoderequiredstring'000000' for surcharge/tip; '090000' ONLY when requesting cash back (debit/EBT).
TransactionAmountrequiredstringThe total: base + surcharge + tip + cashback. 12-digit cents.
AdditionalAmountsrequiredstring20-character positional string (DE 54). Layout below.

AdditionalAmounts is a 20-character positional string:

PosFieldValue
1-2Account type"00" default account
3-4Amount type"42" surcharge · "43" tip · "40" cashback · "39" cumulative incremental-auth amount
5-7Currency code"840" (USD)
8Credit / debit"C" credit · "D" debit
9-20Amount (cents)12 digits, zero-padded. "000000000500" = $5.00

Surcharge rules: max 3% of the base amount. Your terminal must vet the BIN before applying – if you send a surcharge on a debit card BIN, Cygma returns response code 97 “Surcharge Not Permitted”. Re-prompt the user and resubmit without the surcharge.

Cashback is only valid on debit / EBT (the "D" credit-debit flag); never on credit. Surcharge is only valid on credit ("C"); never on debit.

Incremental authorization uses AdditionalAmounts amount type "39" – the complete flow with packets is in the next section.

↑ Request – what you send
// Complete sale: $166.66 base + $5.00 surcharge = $171.66.
// ProcessingCode stays "000000" for surcharge and tip —
// ONLY cash back flips it to "090000". Sending 090000 on
// a credit surcharge sale is rejected as FE (code 30).
// TransactionAmount includes the surcharge.
// POST /hostapi/saledebitebt/

{
  "MessageType": "0200",
  "ProcessingCode": "000000",
  "TransactionAmount": "000000017166",
  "AdditionalAmounts": "0042840C000000000500",
  "LocalTransactionTime": "134512",
  "LocalTransactionDate": "08/23",
  "SystemsTraceNumber": "123456",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "AcquirerId": "<ACQUIRER_ID>",
  "PrimaryAccountNumber": "4111111111111111",
  "ExpirationDate": "2812",
  "RequestDataElements": {
    "BatchNumber": "00001",
    "CardType": "CR",
    "HardwareVendorIdentifier": "FISP",
    "SoftwareIdentifier": "0002",
    "MarketSpecificDataRequest": " ",
    "RequestedACI": "Y",
    "ZipCode": "11933",
    "Address": "7 MAIN ST",
    "CVV2RequestValue": "11123 "
  }
}

// After the customer adds a $35 tip on the receipt, send a
// Tip Adjust with the new total:

{
  "TransactionAmount":  "000000020666",
  "AdditionalAmounts":  "0043840C000000003500",
  ...
}

// $30 debit sale + $10 cashback request:

{
  "TransactionAmount":  "000000003000",
  "AdditionalAmounts":  "0040840D000000001000",
  ...
}
Hospitality flow · open tab → increments → close → tip

Incremental auth + tip adjust

The bar-tab / hospitality lifecycle: open a tab with a pre-authorization, raise it with incremental authorizations as the tab grows, capture the final amount with a completion, then adjust for the written tip.

Rules for the incremental messages (steps 2-3):

  • RequestDataElements.ReasonCode: "10" (Incremental Authorization) is mandatory.
  • AdditionalAmounts amount type "39" carries the cumulative authorized total; TransactionAmount is the additional amount being authorized in this message.
  • The original RetrievalReferenceNumber (DE 37) must be present and match – response code 98 “Incremental Transaction Validation Error” means DE 54 was missing or the original RRN was missing/mismatched.
  • Visa: set RequestedACI: "I" (Incremental Payments).

The completion (step 4) and tip adjust (step 5) reference the transaction by AuthorizationIdResponse + OriginalSystemTraceAuditNumber+ the original’s exact transmitted date/time. Tip adjust is only valid while the sale is in the OPEN batch.

There are two ways to close a tab and they are not equivalent. Putting the tip inside the completion is a single message and is the right close whenever the final amount is known at capture time - authorize with headroom (20% over the running tab is customary) and capture the total. Reach for /adjustsale/ only when the amount changes after the capture has already happened.

Do not use /adjustsale/ to raise an authorization. Adjust does not route to the card brand - it is a host-local batch edit whose amount reaches the brand at clearing - so the issuer’s hold does not move. Growing the tab that way would settle above the authorized amount with no authorization behind the difference. Raising a tab is what the incremental authorizations in steps 2-3 are for.

Run the full sequence against the cert host before production.

↑ Request – what you send
// STEP 1 – Open the tab: authorization $10.00.
// POST /hostapi/authorization/
//
// An authorization and a pre-authorization are the SAME packet on
// this host; only the endpoint differs, and each exists for its own
// reason. Use /authorization when you will raise and then complete
// the same approval (the bar tab below). Use /preauthorization when
// you are holding an estimated amount you will capture later. The
// certified hospitality run below is the authorization path.

{
  "MessageType": "0100",
  "ProcessingCode": "100000",
  "TransactionAmount": "000000001000",
  "LocalTransactionTime": "193000",
  "LocalTransactionDate": "08/23",
  "SystemsTraceNumber": "100001",
  "POSEntryMode": "012",
  "POSConditionCode": "08",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "PrimaryAccountNumber": "4111111111111111",
  "ExpirationDate": "2812",
  "RequestDataElements": {
    "BatchNumber": "00001",
    "CardType": "CR",
    "HardwareVendorIdentifier": "FISP",
    "SoftwareIdentifier": "0002",
    "MarketSpecificDataRequest": " ",
    "RequestedACI": "Y",
    "ElectronicCommerceIndicator": "01",
    "InvoiceERCReferenceNumber": "TAB4021"
  }
}

// Response — SAVE these for every later message:
//   "AuthorizationIdResponse": "012345"        (DE 38)
//   "RetrievalReferenceNumber": "425123456789" (DE 37)
//   plus your STAN "100001" and date/time.
RequestDataElements.CVV2RequestValue · result DE63.A42

CVV2 verification

Send the card security code in CVV2RequestValue inside RequestDataElements. It is not a bare 3–4 digit value – it is a fixed 6-character field. Sending the raw code (e.g. "123") is the classic 30 Format error on Mastercard and a suspect-fraud decline on Visa.

FieldTypeDescription
RequestDataElements.CVV2RequestValueoptionalstringFixed 6-character control field: presence flag + response-request flag + the code, left-justified and space-padded. Layout below.
PositionMeaningValues
1Presence1 = code was sent · 0 = not provided
2Response request1 = return the response code and the CVV2 result code
3–6The code3-digit CVV2 (Visa / MC / Discover) or 4-digit CID (Amex), left-justified and space-padded to 4. All four spaces when not provided.

The issuer's answer is normalized into CVV2ResultCode (DE63.A42):

CodeMeaning
MMatch
NNo match
PNot processed
SMerchant flagged the code as not present, but it should be on the card
UIssuer not certified / unable to verify

The digit length and card-brand name for the code differ by network, but every brand normalizes to the same M/N/P/S/U result above. The raw per-network code is echoed in CVV2NetworkResultCode.

Card typeNameLengthNormalized result
VisaCVV23M / N / P / S / U
MastercardCVC23M / N / P / S / U
DiscoverCID3M / N / P / S / U
American ExpressCID4M / N / P / S / U
↑ Request – what you send
// CVV2RequestValue is a fixed 6-character control field – NOT a bare code:
//   pos 1     presence   "1" = sent · "0" = not provided
//   pos 2     response   "1" = return the response code AND the CVV2 result
//   pos 3-6   the code   left-justified, space-filled to 4

// Visa / Mastercard / Discover — 3-digit CVV2 "123"
"RequestDataElements": { "CVV2RequestValue": "11123 " }

// American Express — 4-digit CID "1234"
"RequestDataElements": { "CVV2RequestValue": "111234" }

// Not on hand — presence 0, still ask for the result
"RequestDataElements": { "CVV2RequestValue": "01    " }

// Result comes back normalized in CVV2ResultCode (DE63.A42):
"ResponseDataElements": { "CVV2ResultCode": "M" }
RequestDataElements.ZipCode + .Address · result AVSResponseCode

AVS verification

Submit the cardholder's billing ZIP in ZipCode and the street in Address, inside RequestDataElements. AVS runs whenever either is present – there is no explicit AVS request flag to set. The issuer's answer returns in AVSResponseCode; the raw per-network code is in AVSNetworkResultCode.

FieldTypeDescription
RequestDataElements.ZipCodeoptionalstringCardholder billing ZIP. AVS runs whenever this is present.
RequestDataElements.AddressoptionalstringCardholder billing street. AVS runs whenever this is present.
CodeMeaning
MAddress + ZIP match
YAddress + ZIP (5-digit) match
XAddress + ZIP (9-digit) match
AAddress matches, ZIP does not
ZZIP matches, address does not
WZIP (9-digit) matches, address does not
PPostal matches, street not verified
BStreet matches (intl), postal not verified
DAddress + ZIP match (intl)
NNo match – neither address nor ZIP
IAddress not verified (intl)
GNon-U.S. issuer, not verified
RRetry – issuer system unavailable
SAVS not supported by issuer
UAddress information unavailable
EAVS error / not allowed for this transaction
↑ Request – what you send
// AVS is triggered by the PRESENCE of the billing fields – there is
// no separate AVS request flag. Send either or both:
"RequestDataElements": {
  "ZipCode": "90210",
  "Address": "1 Market St"
}

// The issuer's answer comes back in AVSResponseCode
// (raw per-network code echoed in AVSNetworkResultCode):
"ResponseDataElements": {
  "AVSResponseCode": "Y",
  "AVSNetworkResultCode": "..."
}
POST /hostapi/settlementrequest/

Batch settlement

Cygma settles by batch. At end of business day, post your own tallied totals to /hostapi/settlementrequest/MTI 500, ProcessingCode 920000. Cygma compares them against what it captured during the day.

POST/hostapi/settlementrequest/Reconcile – post your tallied batch totals. MTI 500, ProcessingCode 920000. 00 = settled; 95 = mismatch.
POST/hostapi/batchupload/On a 95: upload each captured transaction, one POST per capture. MTI 320, same BatchNumber.
POST/hostapi/settlementtrailer/Close – re-send the trailer with the same totals as step 1. MTI 500.
  • ResponseCode 00 – totals match; the batch is closed. Done.
  • ResponseCode 95totals mismatch. Cygma's tape and yours disagree, so it needs the itemized batch to reconcile.

On a 95, run the two-step upload flow:

  1. Post one /hostapi/batchupload/ (MTI 320) for each captured transaction in the batch – the same BatchNumber, echoing the original SystemsTraceNumber, ProcessingCode, TransactionAmount, and AuthorizationIdResponse.
  2. Close with /hostapi/settlementtrailer/ (MTI 500) carrying the same count/amount totals you sent in step 1. Once the uploaded items add up to the trailer, Cygma returns 00 and the batch settles.

The totals block is four count / amount pairs. Amounts are 12-digit, zero-padded, in minor units (cents):

FieldTypeDescription
RequestDataElements.CreditCardSalesCountrequiredstringNumber of credit sales in the batch.
RequestDataElements.CreditCardSalesAmountrequiredstringTotal credit sales, 12-digit cents.
RequestDataElements.CreditCardRefundCountrequiredstringNumber of credit refunds.
RequestDataElements.CreditCardRefundAmountrequiredstringTotal credit refunds, 12-digit cents.
RequestDataElements.DebitCardSalesCountoptionalstringNumber of debit sales.
RequestDataElements.DebitCardSalesAmountoptionalstringTotal debit sales, 12-digit cents.
RequestDataElements.DebitCardRefundCountoptionalstringNumber of debit refunds.
RequestDataElements.DebitCardRefundAmountoptionalstringTotal debit refunds, 12-digit cents.
↑ Request – what you send
curl -X POST https://api.cygma.com:443/hostapi/settlementrequest/ \
  -H "Content-Type: application/json" \
  -d '{
  "MessageType": "500",
  "ProcessingCode": "920000",
  "TransactionAmount": "000000000000",
  "SystemsTraceNumber": "000999",
  "TerminalId": "<TERMINAL_ID>",
  "CardAcquirerId": "<YOUR_CARD_ACQUIRER_ID>",
  "SecurityControlInformation": "<SCI>",
  "RequestDataElements": {
    "CreditCardSalesCount": "12",
    "CreditCardSalesAmount": "000000054200",
    "CreditCardRefundCount": "1",
    "CreditCardRefundAmount": "000000002500"
  }
}'
DE 39

Response codes

ResponseCode follows the ISO 8583 standard – "00" is the only happy path. "10" (partial approval) is conditionally happy: Cygma approved a smaller amount than you requested; you ll find the approved amount in TransactionAmount on the response.

On a non-approved response, ResponseDataElements.AlternteResponseText (note the typo in the spec) often carries a human-readable message from the issuer.

↑ Request – what you send
// "00" = approved. Everything else needs handling.
// The full table lives in src/lib/external/cygma/codes.ts.

00  Approved
10  Partial approval
05  Do not honor
12  Invalid transaction
13  Invalid amount
14  Invalid card number
30  Format error
51  Insufficient funds
54  Expired card
55  Incorrect PIN
57  Transaction not permitted to cardholder
61  Exceeds withdrawal limit
78  Invalid CVV2
91  Issuer or switch inoperative
94  Duplicate transaction
95  Reconcile error (run Batch Upload)
99  Unknown error
POS terminals · mobile SDKs

Card entry: MSR / EMV

For POS terminals with a magstripe reader (or an EMV reader falling back to magstripe), pass the raw track data in Track1Data / Track2Data and update POSEntryMode:

  • 010 manual / key-entered (default)
  • 021 Track 2 read
  • 022 Track 1 read
  • 050 EMV chip
  • 070 contactless
  • 090 EMV fallback to magstripe

EMV chip & contactless – for chip reads (dip or tap), send the ICC TLV payload from the kernel in ICCSystemRelatedData (DE 55) as a hex string, alongside the chip’s track-2 image in Track2Data, with POSEntryMode 050 (contact), 070 (contactless) or 090 (fallback to magstripe). Devices that encrypt at the read head (DUKPT / P2PE) also send the KeySerialNumber in RequestDataElements; end-to-end P2PE envelopes ride DE 60 (P2PE). PIN debit adds PINData (DE 52) + SecurityControlInformation (DE 53).

FieldTypeDescription
Track2DataoptionalstringRaw track 2 data from a swipe (or the chip's track-2 image on an EMV read). Omit PrimaryAccountNumber when this is sent.
Track1DataoptionalstringRaw track 1 data (DE 45; less common – some MSRs send both).
ICCSystemRelatedDataoptionalstringDE 55 – the EMV ICC TLV payload (hex) from the chip kernel. Required for chip / contactless reads.
RequestDataElements.KeySerialNumberoptionalstring3DES DUKPT Key Serial Number when the capture device encrypts (P2PE).
POSEntryModerequiredstringSet to match the read type: '021' track 2, '022' track 1, '050' EMV chip, '070' contactless, '090' EMV fallback.
↑ Request – what you send
// Track 2 swipe – pass the raw track, omit PAN.
{
  "MessageType": "200",
  "Track2Data": ";4242424242424242=29121011000000?",
  "POSEntryMode": "021",
  ...
}

// Track 1 — less common; some MSRs send both.
{
  "MessageType": "200",
  "Track1Data": "%B4242424242424242^DOE/JANE^29121010000?",
  "POSEntryMode": "022",
  ...
}

// EMV chip / contactless — DE 55 TLV (hex) + the chip's
// track-2 image; DUKPT KSN when the reader encrypts.
{
  "MessageType": "200",
  "ICCSystemRelatedData": "9F2608AB12…5F340101",
  "Track2Data": ";4242424242424242=29121011000000?",
  "POSEntryMode": "050",
  "RequestDataElements": {
    "KeySerialNumber": "FFFF9876543210E00001",
    ...
  },
  ...
}
apicert-sandbox.cygma.com:9443

Sandbox

Cert sandbox lives at apicert-sandbox.cygma.com:9443 (the same port as production, just a different host). Same JSON shape, same response codes, no real money moves. Ask EPI for sandbox credentials.

↑ Request – what you send
# The same shape works in cert as in production – flip the host.
curl -X POST https://apicert-sandbox.cygma.com:9443/hostapi/saledebitebt/ \
  -H "Content-Type: application/json" -H "Api-Version: 1" \
  -d '{ "MessageType": "200", ... }'

# Approved test card (echo only – never reaches issuers):
#   PAN:        4005529091234562
#   Expiration: 31/12  (Dec 2031)