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.
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
Quickstart
- Get your
CardAcquirerId,TerminalId, andSecurityControlInformationfrom EPI. The first two go in plaintext per request; the third is the auth secret. - POST your transaction JSON to the matching URL (one URL per transaction type – see Sale below for the canonical shape).
- Read
ResponseCodefrom the response."00"means approved; anything else, look up in Response codes.
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>"
}'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.
// 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
}Message envelope
Every transaction uses the same outer shape. A few details that aren’t obvious from the spec PDFs:
MessageTypeis the 3-digit form ("200"), not the canonical ISO four-digit MTI."0200"returns a genericErrorCode: 100000 / "Server error"without ever reaching the ISO layer.LocalTransactionDateandExpirationDateneed slashes on input (MM/DD,YY/MM). The gateway echoes back the no-slash form in responses but rejects unsigned input.TransactionAmountis 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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | 3-digit MTI (e.g. '200') – not the 4-digit ISO form. |
ProcessingCoderequired | string | 6 digits; selects the transaction type. |
TransactionAmountrequired | string | 12 digits, zero-padded, in cents. '000000000400' = $4.00. |
LocalTransactionTimerequired | string | Local time as hhmmss. |
LocalTransactionDaterequired | string | MM/DD, with a slash on input. |
SystemsTraceNumberrequired | string | 6-digit STAN, unique per terminal per business day. |
ExpirationDaterequired | string | Card expiry as YY/MM, with a slash on input. |
POSEntryModerequired | string | How the card was captured (DE 22). |
POSConditionCoderequired | string | POS condition (DE 25). |
TerminalIdrequired | string | Per-merchant terminal id (DE 41), assigned by EPI. |
CardAcquirerIdrequired | string | Acquirer / parent merchant number (DE 42), assigned by EPI. |
SecurityControlInformationrequired | string | Per-terminal secret (DE 53). Treat like a bearer token. |
PrimaryAccountNumberrequired | string | Card number (PAN). Omit when sending track or token data. |
RequestDataElementsoptional | object | Optional / conditional fields – Cygma's 'Private Use' element. |
RequestDataElements.InvoiceReferenceNumberoptional | string | Your order / invoice reference. |
RequestDataElements.HardwareVendorIdentifieroptional | string | Terminal hardware vendor id. |
RequestDataElements.SoftwareIdentifieroptional | string | Integration software id. |
RequestDataElements.CardTypeoptional | string | Card type hint (e.g. 'CR'). |
RequestDataElements.ElectronicCommerceIndicatorrequired | string | '01' on every card-not-present transaction. Any other value downgrades the interchange. |
{
"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"
}
}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.
| Scenario | POSEntryMode | POSConditionCode |
|---|---|---|
| EMV chip read | 051 | 00 |
| Contactless / Apple Pay / Google Pay | 071 | 00 |
| Swipe (EMV fallback) | 801 | 00 |
| Key-entered, card present | 011 | 71 |
| Key-entered, CNP (MOTO) | 012 | 08 |
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.
// 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" } }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.
| Path | What 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. |
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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | Always '200' for a sale. |
ProcessingCoderequired | string | '000000' – debit sale. |
PrimaryAccountNumberrequired | string | Card number (PAN). |
RequestDataElements.InvoiceReferenceNumberoptional | string | Your order reference, stored on the transaction. |
RequestDataElements.ZipCodeoptional | string | Billing ZIP – its presence triggers AVS. See AVS verification. |
RequestDataElements.CVV2RequestValueoptional | string | 6-character CVV2 control field. See CVV2 verification. |
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 "
}
}'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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | Always '200'. |
ProcessingCoderequired | string | '000000'. |
POSEntryModerequired | string | '012' – key-entered, cardholder not present. |
POSConditionCoderequired | string | CNP / MOTO condition code (e.g. '08'). |
PrimaryAccountNumberrequired | string | Card number (PAN). |
RequestDataElements.ElectronicCommerceIndicatorrequired | string | ECI for the channel (e.g. '01', or '07' for a TLS-secured channel). |
RequestDataElements.CardHolderFirstNameoptional | string | Cardholder first name. |
RequestDataElements.CardHolderLastNameoptional | string | Cardholder last name. |
RequestDataElements.ZipCodeoptional | string | Billing ZIP for AVS. |
RequestDataElements.Addressoptional | string | Billing street for AVS. |
RequestDataElements.CVV2RequestValueoptional | string | 6-character CVV2 control field. |
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 "
}
}'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.
/hostapi/preauthorization/Step 1 – reserve funds at the high-water amount. MTI 0100, ProcessingCode 100000./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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | '0100' for the pre-auth; '0220' for the completion. |
ProcessingCoderequired | string | '100000' on the pre-auth; '110000' on the completion. |
TransactionAmountrequired | string | Pre-auth: the high-water hold amount. Completion: the final captured amount. 12-digit cents. |
AuthorizationIdResponserequired | string | Auth code from the pre-auth; required on the completion to match. |
RequestDataElements.OriginalSystemTraceAuditNumberrequired | string | STAN of the original pre-auth (completion only). |
RequestDataElements.OriginalTransactionDaterequired | string | Date of the original pre-auth as slashed YY/MM/DD. A bare MMDD is rejected. |
RequestDataElements.OriginalTimerequired | string | hhmmss of the original pre-auth, exactly as transmitted. |
RequestDataElements.OriginalMessageTyperequired | string | 3-digit MTI of the original ('100'). The 4-digit form '0100' is rejected. |
RequestDataElements.OriginalAmountrequired | string | The ORIGINAL authorized amount, 12-digit cents - not the amount being captured. |
RequestDataElements.AuthorizedAmountrequired | string | Same 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.
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"
}
}'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.
/hostapi/onlinerefund/Refund when the original transaction is in a different batch (the common case). MTI 200./hostapi/offlinerefund/Refund within the same batch as the original. MTI 220.| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | Still '200' – same as a sale. |
ProcessingCoderequired | string | '200000' – credit rather than debit. |
TransactionAmountrequired | string | Amount to credit back, 12-digit cents. |
PrimaryAccountNumberrequired | string | Card to refund. |
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"
}'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.
/hostapi/reversalfull/Full reversal. MTI 0400. TransactionAmount is the original amount, or the cumulative amount when reversing an incremented authorization./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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | '0400' full reversal; '0420' partial reversal. Four digits. |
ProcessingCoderequired | string | '000000'. |
TransactionAmountrequired | string | Amount to reverse, 12-digit cents. |
ResponseCodeoptional | string | 0420 only: the original's response code ('00'). Do not send it on the 0400. |
AuthorizationIdResponserequired | string | Auth code of the original transaction. |
RetrievalReferenceNumberrequired | string | DE 37 of the original. The strongest match key. |
PrimaryAccountNumber / ExpirationDaterequired | string | The card, formatted as the original sent it. Use TokenizationElements.Token when the original carried a token. |
RequestDataElements.ReasonCoderequired | string | '02' void/cancel; '01' timeout reversal. |
RequestDataElements.OriginalSystemTraceAuditNumberrequired | string | STAN of the original, verbatim. |
RequestDataElements.OriginalMessageTyperequired | string | MTI of the original, four digits ('0200' sale, '0100' authorization). |
RequestDataElements.OriginalProcessingCoderequired | string | ProcessingCode of the original ('000000' sale, '100000' authorization). |
RequestDataElements.OriginalAmountrequired | string | Original transaction amount, 12-digit cents. |
RequestDataElements.OriginalTransactionDate / OriginalTimerequired | string | 'YY/MM/DD' and 'HHMMSS' as the original was sent. |
RequestDataElements.NetworkReferenceNumberoptional | string | From the original response, when present. |
RequestDataElements.AuthorizedAmountoptional | string | 0420 only: the original amount, 12-digit cents. |
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": ""
}
}'Balance inquiry
Inquires the available balance on a card. No transaction is recorded – pure read.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | '100'. |
ProcessingCoderequired | string | '310000' – balance inquiry. |
TransactionAmountrequired | string | All zeros – read-only, no amount is moved. |
PrimaryAccountNumberrequired | string | Card to inquire. |
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>"
}'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.
| Field | Type | Description |
|---|---|---|
MessageTyperequired | string | '0100'. |
ProcessingCoderequired | string | '380000' – the card-verification family (38aa0x). '000000' answers 12 INVALID TRANSACTION. |
TransactionAmountrequired | string | All zeros – required to be zero for card verification. No hold is placed. |
PrimaryAccountNumberrequired | string | Card to verify. |
ExpirationDaterequired | string | Card expiry as YY/MM, with a slash. |
RequestDataElements.ZipCodeoptional | string | Billing ZIP for AVS. |
RequestDataElements.CVV2RequestValueoptional | string | 6-character CVV2 control field. |
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 "
}
}'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.
| Field | Type | Description |
|---|---|---|
TokenizationElements.Tokenoptional | string | Token (T03, 13–19 digits) standing in for the PAN; send in place of PrimaryAccountNumber on voids, refunds, and recurring charges. |
TokenizationElements.TokenRequestorIdoptional | string | TRID (T07) – 11-digit token requestor identifier, echoed on token responses. |
TokenizationElements.TokenIndicatoroptional | string | Inline option (unverified): 'R' on a sale/auth request asks the host to return a token in the response. Prefer the dedicated tokenize service. |
// 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" },
...
}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.
| Field | Type | Description |
|---|---|---|
SalesTaxAmountrequired | L11 · M | Total state sales tax, 12-digit zero-padded cents ('000000000150' = $1.50). |
SalesTaxCollectedIndicatorrequired | L12 · C99 | '0' = no tax information · '1' = tax amount provided · '2' = tax exempt / non-taxable. There is no separate TaxExemptIndicator field – exemption is this value. |
MerchantOrderCustomerReferenceNumberrequired | L24 | Invoice / reference number (alphanumeric, max 17). REQUIRED whenever SalesTaxAmount (L11) is sent. |
CustomerCodeoptional | L10 · O | Code the cardholder supplies to the merchant – commonly the card's last 4. Alphanumeric, max 38. Visa / MC / Amex / Discover. |
CommercialRequestIndicatoroptional | L01 · O | '1' requests commercial-card qualification ('0' = no request). Visa. |
OrderDateoptional | L21 · C | Date the order was placed, YYMMDD. |
DiscountAmountrequired | L14 · C99 | Total discount applied, 12-digit cents. |
DiscountAmountCreditDebitIndicatorrequired | L26 · C99 | Credit/debit indicator for DiscountAmount. |
FreightShippingAmountrequired | L15 · C99 | TOTAL freight / shipping and handling, 12-digit cents. Header-level – never per line item. |
FreightShippingAmountCreditDebitIndicatorrequired | L27 · C99 | Credit/debit indicator for FreightShippingAmount. |
DutyAmountrequired | L16 · C99 | Import/export duty amount, 12-digit cents. |
DutyAmountCreditDebitIndicatorrequired | L25 · C99 | Credit/debit indicator for DutyAmount. |
SalesTaxRateoptional | L09 | Sales tax rate (5 chars), optional. |
DestinationCountryCodeoptional | L17 | Country where goods will be delivered ('USA'). |
ShipFromPostalCodeoptional | L18 | Postal code goods ship FROM (merchant), max 10. |
DestinationPostalCodeoptional | L19 | Postal code goods ship TO (customer), max 10. |
ElectronicCommerceIndicatoroptional | I02 | '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".
// 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)
}
}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 Level3Items1…Level3Items15 (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.
| Field | Type | Description |
|---|---|---|
Level3Items1 … Level3Items15required | L99 · C | One 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.
// 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"
}
}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
| Field | Type | Description |
|---|---|---|
Aoptional | prefix | Common Data Elements (e.g. A01 InvoiceERCReferenceNumber). |
Ioptional | prefix | Electronic Commerce (I02 ElectronicCommerceIndicator). |
Joptional | prefix | Industry Specific Data – lodging, fleet, bill payment, EBT, telecom. |
Hoptional | prefix | Hotel/Lodging industry data. Only 4-char tag prefix – all others are 3. |
Loptional | prefix | Commercial Card Level II and Level III. |
Moptional | prefix | Miscellaneous. |
Poptional | prefix | Network Specific Fields. |
Roptional | prefix | Response Fields. |
Soptional | prefix | Reconciliation Totals (settlement). |
Toptional | prefix | Token Data (T03 Token, T07 TokenRequestorId). |
Condition Code/Meaning chart
| Field | Type | Description |
|---|---|---|
Moptional | condition | Mandatory. |
Ooptional | condition | Optional. |
Coptional | condition | Conditional – required when its trigger condition applies. |
C01optional | condition | PAN present in DE 2 when no track data is present (keyed entry). |
C02optional | condition | Expiration date included when the card number was entered manually. |
C03optional | condition | Track I and/or II included when the card is read from the magnetic stripe. |
C05optional | condition | POS Entry Mode: first two digits '01' keyed / '02' card reader; last digit '1' PIN capable / '2' no PIN. |
C06optional | condition | Keyed account number ⇒ DE 2 included; swiped ⇒ track data included. |
C07optional | condition | EMV: terminal with ICC capability performing a chip transaction. |
C99optional | condition | REQUIRED 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)
| Field | Type | Description |
|---|---|---|
L01 CommercialRequestIndicatoroptional | an 1 · O | '1' = request commercial card status, '0' = no request. Visa. |
L02 CommercialCardResponseIndicatoroptional | an 1 · C | Response-side: whether the card is Business / Corporate / Purchase. Visa. |
L09 SalesTaxRateoptional | an 5 · O | Sales tax rate. |
L10 CustomerCodeoptional | an 38 · O | Cardholder-supplied code (commonly card last 4). All brands. |
L11 SalesTaxAmountrequired | un 12 · M | Total state sales tax, zero-padded cents. The core Level II amount. |
L12 SalesTaxCollectedIndicatorrequired | an 1 · C99 | '0' no tax info · '1' tax provided · '2' tax exempt / non-taxable. |
L13 AltTaxAmountoptional | an 12 · O | Alternate tax amount. |
L14 DiscountAmountrequired | un 12 · C99 | Total discount applied. MC Common Data p0732. |
L15 FreightShippingAmountrequired | un 12 · C99 | TOTAL freight/shipping. MC p0606. |
L16 DutyAmountrequired | an 12 · C99 | Import/export duty total. MC p0607. |
L17 DestinationCountryCodeoptional | an 3 · C | Delivery country. Amex DF63/DF47, MC p0610. |
L18 ShipFromPostalCodeoptional | an ..10 · C | Ship-from (merchant) postal code. MC p0613. |
L19 DestinationPostalCodeoptional | an ..10 · C | Ship-to (customer) postal code. Amex DF63. |
L20 UniqueVATInvoiceReferenceNumberoptional | an ..15 · C | Unique VAT invoice reference. |
L21 OrderDateoptional | un 6 · C | Order date YYMMDD. MC P0614. |
L24 MerchantOrderCustomerReferenceNumberrequired | an ..17 | Invoice/reference number. REQUIRED when L11 SalesTaxAmount is sent. |
L25 DutyAmountCreditDebitIndicatorrequired | un 1 · C99 | D/C indicator for L16. MC Duty Amount Sign. |
L26 DiscountAmountCreditDebitIndicatorrequired | an 1 · C99 | D/C indicator for L14. |
L27 FreightShippingAmountCreditDebitIndicatorrequired | an 1 · C99 | D/C indicator for L15. |
L44 ShipDateoptional | un 6 · O | Date merchandise shipped. MasterCard. |
L-tag dictionary – repeating line-item fields (inside Level3Items TLV only; ignored at header)
| Field | Type | Description |
|---|---|---|
L30 ItemCommodityCoderequired | an 15 · O | Commodity 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 ProductCoderequired | an ..12 · C99 | Product code / SKU of the item. Amex DF47 s12, MC p0641. |
L32 ItemDescriptionrequired | an ..26 · C99 | Description of the purchased item. MC p0642. |
L33 ItemQuantityrequired | un 12 · O | Number of items purchased (9-digit zero-padded in practice). All brands. MC p0643. |
L34 ItemUnitOfMeasurerequired | an ..12 · C99 | International trade unit code ('EA' each). MC p0645. |
L35 ExtendedItemAmountrequired | un 12 · C99 | Line total (qty × unit cost − discount). MC p0647 s1. |
L36 ExtendedAmountCreditDebitIndicatorrequired | an 1 · C99 | 'D' debit (normal sale line) / 'C' credit. MC p0647 s3. |
L37 DiscountAmountPerLineItemrequired | un 12 · C99 | Discount applied at line level. MC p0648 s2. |
L38 ItemDiscountIndicatoroptional | an 1 · C | 'Y' line was discounted / 'N' not. MC p0648 s1. |
L39 ZeroCostToCustomerIndicatoroptional | a 1 · C | 'Y' item provided at no cost / 'N'. MC p0650. |
L40 UnitCostrequired | un 12 · C99 | Unit price of the item. MC p0646. |
L41 VATRateAppliedoptional | un 5 · O | VAT rate applied to the line. |
L42 VATTaxTypeoptional | an 4 · O | Type of value-added tax. |
L43 VATTaxAmountoptional | un 12 · O | VAT amount for the line. |
L45 ShippingMethodoptional | an 2 · O | 0100/0200 only: '01' same day · '02' overnight · '03' priority 2-3d · '04' ground · '05' electronic · '06' ship-to-store. |
L46-L49 ShipTo Name/Address/Phoneoptional | an · O | Amex 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/Endoptional | O | Discover promotional code (6) + start/end dates (8). |
Required tags to build a Level II packet – all card brands
| Field | Tag | Value |
|---|---|---|
| MarketSpecificDataRequest | – | Single space " " |
| RequestedACI | – | "Y" |
| SalesTaxAmount | L11 | 12-digit cents |
| SalesTaxCollectedIndicator | L12 | "1" collected · "2" exempt · "0" none |
| MerchantOrderCustomerReferenceNumber | L24 | Invoice/reference number (alnum ≤17) |
| ElectronicCommerceIndicator | I02 | "01" – CNP transactions only |
Required tags to build a Level III packet – all card brands (in addition to the Level II set)
| Where | Tags |
|---|---|
| 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 string | L30 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
| Tag | Visa | Mastercard | Discover | Amex |
|---|---|---|---|---|
| L10 | ✓ | Line Item p0508 | ✓ | ✓ |
| L11 | ✓ | ✓ | SDR 10 | ✓ |
| L14 / L26 | ✓ | Common Data p0732 | ✓ | ✓ |
| L15 / L27 | ✓ | Common Data p0606 | ✓ | ✓ |
| L16 / L25 | ✓ | Common Data p0607 | ✓ | ✓ |
| L17 | ✓ | p0610 | MOTO SDR | DF63 206-208 |
| L18 / L19 | ✓ | p0613 | Geographic SDR | DF63 92-100 |
| L30 | ✓ | p0679 | ✓ | – |
| L31 | ✓ | Line Item p0641 | SDR | DF47 s12 / s3 |
| L32 | ✓ | Line Item p0642 | SDR | ✓ |
| L33 | ✓ | Line Item p0643 | ✓ | ✓ |
| L34 | ✓ | Line Item p0645 | SDR | ✓ |
| L35 / L36 | ✓ | Line Item p0647 | ✓ | ✓ |
| L37 / L38 | ✓ | Line Item p0648 | SDR | ✓ |
| L39 | ✓ | Line Item p0650 | ✓ | ✓ |
| L40 | ✓ | Line Item p0646 | SDR | ✓ |
| L44 | – | ✓ | – | – |
| L45–L49 | – | – | – | DF47 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.
// 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 costIndustry 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.
| Field | Type | Description |
|---|---|---|
FUELoptional | J26 = 8 | Fuel — Pump and product detail for a fuel purchase. Mastercard and Discover carry different subsets; Visa fuel rides the Fleet profile. 14 fields. |
AIRLINEoptional | J26 = 1 | Airline — Ticket, passenger and itinerary detail. Captures the ticket header and the first air segment. 29 fields. |
HEALTHCAREoptional | J26 = 18 | Healthcare — Provider and payer identifiers. Per-line healthcare eligibility is set on the Level III grid. 3 fields. |
CRUISEoptional | J26 = 19 | Cruise — Sailing, itinerary and the air leg to the port, plus agency identifiers. 19 fields. |
RAILoptional | J26 = 15 | Rail — Ticket, journey and service detail for rail travel. 19 fields. |
ELECTRIC_FUELoptional | J26 = 26 | Electric vehicle charging — EV charging session — connector, energy, timings and station capacity. Required for MCC 5552 in Europe now and in the US by 2030. 15 fields. |
TRAVELoptional | J26 = 17 | Travel agency — Agency identifiers and the service fee charged on a travel booking. 7 fields. |
INSURANCEoptional | J26 = 22 | Insurance — Policy, insured party and premium detail. 7 fields. |
TELEPHONEoptional | J26 = 14 | Telephone — Originating and destination numbers for a call-based charge. 3 fields. |
TICKET_ENTERTAINMENToptional | J26 = 16 | Ticketing / entertainment — Event, venue and ticket detail. 9 fields. |
VISA_TRANSPORT_ANCILLARYoptional | J26 = 23 | Transport ancillary — Baggage, seating or other purchases attached to a travel document rather than the ticket. 4 fields. |
HOTELoptional | J26 = 4 | Hotel / lodging — Lodging enhanced data - the stay, the room, and itemised incidentals. Carried on all brands. 27 fields. |
AUTO_RENTALoptional | J26 = 6 | Auto rental — Vehicle rental enhanced data - agreement, pickup and return detail, distance and adjustments. 21 fields. |
FLEEToptional | J26 = 25 | Fleet — Visa 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.
| Field | Type | Description |
|---|---|---|
CompanyBrandNameauto-derived | B62 · an4 | Brand — Brand at the pump, 4 characters (e.g. SHEL). Pre-filled from the merchant name. |
PurchaseTimeauto-derived | B63 · n4 · HHMM | Purchase time — Local time at the pump, HH:MM. MC only. |
FuelServiceTypeauto-derived | B64 · an1 · enum | Service type Values: S, F, H. MC only. |
| Field | Type | Description |
|---|---|---|
FuelCoderequired to qualify | B69 · an2 · enum | Fuel code — Visa Fuel Type Code. 121 defined values. DISC only. |
FuelUnitPricerequired to qualify | B71 · n12 · 4dp implied | Price per gallon — Dollars per gallon, e.g. 3.499. MC only. |
FuelQuantityrequired to qualify | B72 · n6 · 3dp implied | Quantity (gallons) — Gallons dispensed, e.g. 12.153. MC only. |
FuelSaleAmountauto-derived | B73 · n12 · 2dp implied | Fuel sale amount — The fuel portion of the sale. MC only. |
| Field | Type | Description |
|---|---|---|
TotalTaxAmountauto-derived | B65 · n12 · 2dp implied | Total tax |
TotalTaxCollectIndicatorauto-derived | B66 · an1 · enum | Tax collected Values: Y, N. MC only. |
StateSaleTaxAmountauto-derived | B67 · n12 · 2dp implied | State sales tax — Pre-filled from the merchant's default tax rate in Settings. DISC only. |
StateSaleTaxIdrequired to qualify | B68 · an1 | State tax ID — One character. DISC only. |
TaxExemptNumberrequired to qualify | B70 · n12 | Tax exempt number — Digits only, up to 12. DISC only. |
| Field | Type | Description |
|---|---|---|
FleetOdometerReadingrequired to qualify | H165 · n7 | Odometer — Whole miles. Digits only - no commas, no decimals. |
| Field | Type | Description |
|---|---|---|
ExemptIndicatorrequired to qualify | — · an1 | Exempt indicator |
AirlineJ26 = 129 fields · All brands
Ticket, passenger and itinerary detail. Captures the ticket header and the first air segment.
| Field | Type | Description |
|---|---|---|
AirlineTicketNumberrequired to qualify | C02 · an15 | Ticket number |
AirlinePassengerNamerequired to qualify | C10 · an25 | Passenger name |
AirlineTransactionTyperequired to qualify | C01 · an2 | Transaction type |
AirlineDocumentTyperequired to qualify | C03 · an2 | Document type |
AirlineTicketIssueDaterequired to qualify | C08 · n8 · YYYYMMDD | Issue date |
AirlineTicketIssueCityrequired to qualify | C07 · an18 | Issue city |
AirlineTicketingCarriernamerequired to qualify | C06 · an25 | Ticketing carrier |
AirlineIATANumericCoderequired to qualify | C05 · n8 | IATA code |
AirlineElectronicTicketIndicatorauto-derived | C14 · an1 · enum | Electronic ticket Values: E, P. |
AirlineRestrictedTicketIndicatorrequired to qualify | C75 · an1 · enum | Restricted ticket Values: N, R. |
AirlineNumberinPartyauto-derived | C09 · n3 | Passengers |
AirlineTotalFareauto-derived | C78 · n12 · 2dp implied | Total fare |
| Field | Type | Description |
|---|---|---|
AirlineTotalNumberAirSegmentsauto-derived | C15 · n2 | Air segments — Total legs on the ticket. Only the first is captured here. |
AirlineDepartureLocationCodeSegmentrequired to qualify | C18 · an5 | From (airport) |
AirlineArrivalLocationCodeSegmentrequired to qualify | C20 · an5 | To (airport) |
AirlineDepartureDateSegmentrequired to qualify | C19 · n8 · YYYYMMDD | Departure date |
AirlineDepartureTimerequired to qualify | C79 · n4 · HHMM | Departure time — HH:MM |
AirlineArrivalTimerequired to qualify | C80 · n4 · HHMM | Arrival time — HH:MM |
AirlineSegmentCarrierCoderequired to qualify | C21 · an4 | Carrier |
AirlineFlightNumberSegmentrequired to qualify | C24 · an6 | Flight number |
AirlineClassServiceCodeSegmentrequired to qualify | C23 · an3 | Class of service |
AirlineSegmentFareBasisrequired to qualify | C22 · an15 | Fare basis |
AirlineSegmentFarerequired to qualify | C25 · n12 · 2dp implied | Segment fare |
AirlineStopOverIndicatorrequired to qualify | C17 · an1 · enum | Stopover Values: O, X. |
| Field | Type | Description |
|---|---|---|
AirlineTravelAgencyCoderequired to qualify | C73 · an8 | Agency code |
AirlineTravelAgencyNamerequired to qualify | C74 · an25 | Agency name |
AirlineCustomerCoderequired to qualify | C71 · an17 | Customer code |
AirlineTicketChangeIndicatorrequired to qualify | C77 · an1 | Ticket change |
AirlineCreditReasonIndicatorrequired to qualify | C76 · an1 | Credit reason |
HealthcareJ26 = 183 fields · All brands
Provider and payer identifiers. Per-line healthcare eligibility is set on the Level III grid.
| Field | Type | Description |
|---|---|---|
VisaHCProviderIDrequired to qualify | P27 · an15 | Provider ID — Visa healthcare provider identifier. VISA only. |
VisaServiceTypeCoderequired to qualify | P28 · an4 | Service type VISA only. |
HealthcarePayerIDrequired to qualify | P31 · an15 | Payer ID |
CruiseJ26 = 1919 fields · All brands
Sailing, itinerary and the air leg to the port, plus agency identifiers.
| Field | Type | Description |
|---|---|---|
CruisePassengerNamerequired to qualify | C38 · an25 | Passenger name |
CruiseTravelTicketNumberrequired to qualify | C39 · an15 | Ticket number |
CruiseNamerequired to qualify | C50 · an25 | Cruise / ship name |
CruiseDepartureDaterequired to qualify | C46 · n8 · YYYYMMDD | Departure date |
CruiseReturnDaterequired to qualify | C47 · n8 · YYYYMMDD | Return date |
CruiseNumberOfDaysrequired to qualify | C49 · n3 | Nights |
CruiseTotalCostauto-derived | C48 · n12 · 2dp implied | Total cost |
CruiseClassCoderequired to qualify | C45 · an3 | Class |
CruiseTravelPackageIndicatorrequired to qualify | C37 · an1 · enum | Travel package Values: Y, N. |
| Field | Type | Description |
|---|---|---|
CruiseDestinationCoderequired to qualify | C41 · an5 | Destination |
CruiseCityNamerequired to qualify | C53 · an18 | City |
CruiseRegionCoderequired to qualify | C51 · an3 | Region |
CruiseCountryCoderequired to qualify | C52 · an3 | Country |
| Field | Type | Description |
|---|---|---|
CruiseDepartureAirportrequired to qualify | C42 · an5 | Departure airport |
CruiseAirCarrierCoderequired to qualify | C43 · an4 | Air carrier |
CruiseFlightNumberrequired to qualify | C44 · an6 | Flight number |
CruiseDepartDaterequired to qualify | C40 · n8 · YYYYMMDD | Flight date |
| Field | Type | Description |
|---|---|---|
CruiseIATACarrierCoderequired to qualify | C35 · an4 | IATA carrier |
CruiseIATAAgencyNumberrequired to qualify | C36 · an8 | IATA agency number |
RailJ26 = 1519 fields · All brands
Ticket, journey and service detail for rail travel.
| Field | Type | Description |
|---|---|---|
RailTransactionTyperequired to qualify | C26 · an2 | Transaction type |
RailTicketNumberrequired to qualify | C27 · an15 | Ticket number |
RailPassengerNamerequired to qualify | C28 · an25 | Passenger name |
RailCarrierCoderequired to qualify | C29 · an4 | Carrier |
RailTicketIssuerNamerequired to qualify | C30 · an25 | Issuer name |
RailTicketIssuerCityrequired to qualify | C31 · an18 | Issuer city |
| Field | Type | Description |
|---|---|---|
RailLineItemSegmentDepartureLocationrequired to qualify | C32 · an5 | From |
RailLineItemSegmentArrivalLocationrequired to qualify | C34 · an5 | To |
RailLineItemSegmentDepartureDaterequired to qualify | C33 · n8 · YYYYMMDD | Departure date |
RailClassrequired to qualify | C60 · an3 | Class |
RailNumberOfAdultsauto-derived | C58 · n3 | Adults |
RailNumberOfChildrenrequired to qualify | C59 · n3 | Children |
| Field | Type | Description |
|---|---|---|
RailTravellerNamerequired to qualify | C54 · an25 | Traveller name |
RailTicketNumrequired to qualify | C55 · an15 | Service ticket number |
RailServiceTyperequired to qualify | C56 · an3 | Service type |
RailServiceNaturerequired to qualify | C57 · an3 | Service nature |
RailServiceAmountrequired to qualify | C61 · n12 · 2dp implied | Service amount |
RailServiceAmountSignrequired to qualify | C62 · an1 · enum | Amount sign Values: D, C. |
RailProcedureIdrequired to qualify | C63 · an8 | Procedure 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.
| Field | Type | Description |
|---|---|---|
ConnectorTyperequired to qualify | S30 · an3 · enum | Connector type 9 defined values. |
UnitOfMeasureauto-derived | S37 · an1 · enum | Unit of measure — Electric sessions bill by kWh or by minute. Values: W, C. |
Productquantityrequired to qualify | S42 · n12 · 4dp implied | Quantity (kWh) |
FleetUnitPricerequired to qualify | S39 · n12 · 4dp implied | Price per kWh |
TotalAmountIncludingTaxauto-derived | S48 · n12 · 2dp implied | Total including tax |
StartTimeChargerequired to qualify | S46 · n4 · HHMM | Charge start — HH:MM |
FinishTimeChargerequired to qualify | S47 · n4 · HHMM | Charge finish — HH:MM |
TotalChargingTimerequired to qualify | S45 · n6 | Charging time (min) |
TotalTimePluggedinrequired to qualify | S44 · n6 | Plugged in (min) — Can exceed charging time - idle minutes are often billed separately. |
| Field | Type | Description |
|---|---|---|
MaxPowerDispensedrequired to qualify | S31 · n6 | Max power dispensed (kW) |
CharginPowerCapacityrequired to qualify | S36 · n6 | Station capacity (kW) — May exceed max dispensed when the site manages power. |
ChargingReasonCoderequired to qualify | S35 · an3 · enum | Charging reason — Only when the session ended abnormally. 10 defined values. |
| Field | Type | Description |
|---|---|---|
EstMilesAddedrequired to qualify | S34 · n6 | Est. miles added |
EstVehicleMilesAvailablerequired to qualify | S32 · n6 | Est. range on leaving |
CarbonFootprintrequired to qualify | S33 · n12 | Carbon avoided (g CO2e) |
Travel agencyJ26 = 177 fields · All brands
Agency identifiers and the service fee charged on a travel booking.
| Field | Type | Description |
|---|---|---|
TravelAgencyCoderequired to qualify | H101 · an8 | Agency code |
TravelAgencyNameauto-derived | H102 · an25 | Agency name |
TravelAgencySeqNumberrequired to qualify | H085 · an8 | Sequence number |
| Field | Type | Description |
|---|---|---|
TravelAgencyFeeAmountrequired to qualify | H086 · n12 · 2dp implied | Agency fee |
TravelAgencyFeeAmountSignauto-derived | H087 · an1 · enum | Fee sign Values: D, C. |
TravelAgencyFeeAmountRaterequired to qualify | H088 · n6 · 2dp implied | Fee rate (%) |
TravelAgencyFeeDescriptionauto-derived | H089 · an25 | Fee description |
InsuranceJ26 = 227 fields · All brands
Policy, insured party and premium detail.
| Field | Type | Description |
|---|---|---|
InsurancePolicyNumberrequired to qualify | H148 · an25 | Policy number |
AdditionalPolicyNumberrequired to qualify | H152 · an25 | Additional policy number |
TypeOfPolicyrequired to qualify | H153 · an25 | Policy type |
NameOfInsuredrequired to qualify | H154 · an30 | Name of insured |
| Field | Type | Description |
|---|---|---|
InsurancePremiumFrequencyrequired to qualify | H151 · an12 · enum | Premium frequency Values: Monthly, Quarterly, Annual, Single. |
InsuranceAmountauto-derived | H077 · n12 · 2dp implied | Premium amount |
InsuranceIndicatorauto-derived | H131 · an1 · enum | Insurance indicator Values: Y, N. |
TelephoneJ26 = 143 fields · All brands
Originating and destination numbers for a call-based charge.
| Field | Type | Description |
|---|---|---|
CallFromPhoneNumberrequired to qualify | J73 · n15 · digits only | Call from |
CallToPhoneNumberrequired to qualify | J77 · n15 · digits only | Call to |
PhoneCardIdrequired to qualify | J78 · an20 | Phone card ID |
Ticketing / entertainmentJ26 = 169 fields · All brands
Event, venue and ticket detail.
| Field | Type | Description |
|---|---|---|
EventNameauto-derived | J60 · an25 | Event name |
EventDaterequired to qualify | J61 · n8 · YYYYMMDD | Event date |
EventLocrequired to qualify | J64 · an25 | Venue |
EventRegCoderequired to qualify | J65 · an3 | Region |
EventCntryCoderequired to qualify | J66 · an3 | Country |
| Field | Type | Description |
|---|---|---|
EventTktQtyauto-derived | J63 · n4 | Tickets |
EventIndTktPricerequired to qualify | J62 · n12 · 2dp implied | Price per ticket |
TicketTyperequired to qualify | C86 · an4 | Ticket type |
TicketIssuerAddressrequired to qualify | C83 · an25 | Issuer address |
Transport ancillaryJ26 = 234 fields · All brands
Baggage, seating or other purchases attached to a travel document rather than the ticket.
| Field | Type | Description |
|---|---|---|
AncillaryTicketDocumentNorequired to qualify | C65 · an15 | Ticket document number |
AncillaryAdditionalDocumentNorequired to qualify | C69 · an15 | Additional document number |
AncillaryPassengerNamerequired to qualify | C68 · an25 | Passenger name |
AncillaryCreditReasonIndicatorrequired to qualify | C70 · an1 | Credit reason |
Hotel / lodgingJ26 = 427 fields · All brands
Lodging enhanced data - the stay, the room, and itemised incidentals. Carried on all brands.
| Field | Type | Description |
|---|---|---|
HotelArrivalDaterequired to qualify | H016 · n8 · YYYYMMDD | Arrival date |
HotelDepartureDaterequired to qualify | H017 · n8 · YYYYMMDD | Departure date |
HotelFolioNumberrequired to qualify | H018 · an12 | Folio number |
HotelRoomRaterequired to qualify | H021 · n12 · 2dp implied | Room rate (nightly) |
HotelRoomTaxrequired to qualify | H022 · n12 · 2dp implied | Room tax |
HotelNumberOfRoomsBookedauto-derived | H008 · n3 | Rooms booked |
HotelNumberOfAdultsrequired to qualify | H009 · n3 | Adults |
HotelNoShowIndicatorauto-derived | H011 · an1 · enum | No-show Values: N, Y. |
| Field | Type | Description |
|---|---|---|
HotelRoomTyperequired to qualify | H006 · an12 | Room type |
HotelBedTyperequired to qualify | H005 · an12 | Bed type |
HotelRoomLocationrequired to qualify | H004 · an12 | Room location |
HotelSmokingPreferencerequired to qualify | H007 · an1 · enum | Smoking Values: N, S. |
HotelRateTyperequired to qualify | H012 · an12 | Rate type |
HotelProgramCoderequired to qualify | H023 · an12 | Program code |
HotelPromotionalCoderequired to qualify | H001 · an12 | Promotional code |
HotelCorporateClientCoderequired to qualify | H003 · an12 | Corporate client code |
| Field | Type | Description |
|---|---|---|
HotelPhoneChargesrequired to qualify | H024 · n12 · 2dp implied | Phone |
HotelRestaurantRoomServiceChargesrequired to qualify | H025 · n12 · 2dp implied | Restaurant / room service |
HotelMiniBarChargesrequired to qualify | H026 · n12 · 2dp implied | Mini bar |
HotelLaundryChargesrequired to qualify | H027 · n12 · 2dp implied | Laundry |
HotelGiftShopChargesrequired to qualify | H030 · n12 · 2dp implied | Gift shop |
HotelMovieChargesrequired to qualify | H032 · n12 · 2dp implied | Movies |
HotelHealthClubChargesrequired to qualify | H033 · n12 · 2dp implied | Health club |
HotelValetParkingChargesrequired to qualify | H034 · n12 · 2dp implied | Valet parking |
HotelCashDisbursementChargesrequired to qualify | H035 · n12 · 2dp implied | Cash disbursement |
HotelOtherChargesrequired to qualify | H028 · n12 · 2dp implied | Other |
HotelAdjustmentAmountrequired to qualify | H020 · n12 · 2dp implied | Adjustment |
Auto rentalJ26 = 621 fields · All brands
Vehicle rental enhanced data - agreement, pickup and return detail, distance and adjustments.
| Field | Type | Description |
|---|---|---|
RentalAgreementNumberrequired to qualify | B01 · an25 | Agreement number — Rental agreement number signed by the cardholder. |
RentalRateIndicatorrequired to qualify | B38 · an1 · enum | Rate type Values: D, W, M. |
RentalRaterequired to qualify | B39 · n12 · 2dp implied | Rate |
RentalVehicleClassIDrequired to qualify | B14 · an4 | Vehicle class |
RentalDriverTaxNumberrequired to qualify | B22 · an20 | Driver tax number |
| Field | Type | Description |
|---|---|---|
RentalPickupDaterequired to qualify | B06 · n8 · YYYYMMDD | Pickup date |
RentalPickupTimerequired to qualify | B07 · n4 · HHMM | Pickup time — HH:MM |
RentalPickupLocationrequired to qualify | B02 · an26 | Location |
RentalPickupCityNamerequired to qualify | B03 · an18 | City |
RentalPickupRegionCoderequired to qualify | B04 · an3 | State / region |
RentalPickupCountryCoderequired to qualify | B05 · an3 | Country |
| Field | Type | Description |
|---|---|---|
RentalReturnDaterequired to qualify | B11 · n8 · YYYYMMDD | Return date |
RentalReturnTimerequired to qualify | B12 · n4 · HHMM | Return time — HH:MM |
RentalDropofflocationrequired to qualify | B19 · an26 | Drop-off location |
RentalReturnCityNamerequired to qualify | B08 · an25 | City |
RentalReturnRegionCoderequired to qualify | B09 · an3 | State / region |
RentalReturnCountryCoderequired to qualify | B10 · an3 | Country |
RentalDistancerequired to qualify | B15 · n5 | Distance travelled — Whole units. |
RentalDistanceUnitofMeasureauto-derived | B16 · an1 · enum | Distance unit Values: M, K. |
RentalAdjustmentIndicatorrequired to qualify | B17 · an1 | Adjustment type |
RentalAdjustmentAmountrequired to qualify | B18 · n12 · 2dp implied | Adjustment 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.
| Field | Type | Description |
|---|---|---|
BusinessApplicationIdentifierauto-derived | P25 · an2 | Business application — Fleet business application identifier. F1 per Cygma's Visa Fleet sample. |
| Field | Type | Description |
|---|---|---|
FleetFuelTypeoptional | H157 · an2 | Fuel 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. |
| Field | Type | Description |
|---|---|---|
TypeOfPurchaserequired to qualify | S51 · an1 · enum | Type of purchase — MANDATORY on fleet. Drives which of the fields below Visa requires. Values: 1, 2, 3, 4. |
VisaExpandFuelTypeoptional | S25 · an4 · enum | Fuel 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. |
ServiceTypeoptional | S52 · an1 · enum | Service type Values: S, F, H. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - not sent on a non-fuel purchase. |
UnitOfMeasureoptional | S37 · an1 · enum | Unit of measure 7 defined values. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only - not sent on a non-fuel purchase. |
Productquantityoptional | S42 · n12 · 4dp implied | Quantity (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. |
FleetUnitPriceoptional | S39 · n12 · 4dp implied | Price 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. |
GrossFuelPriceoptional | S55 · n12 · 4dp implied | Gross 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. |
FleetNetFuelPriceoptional | H162 · n12 · 4dp implied | Net fuel price — Optional. Quantity x cost EXCLUSIVE of taxes. Applies only when TypeOfPurchase is “1” or “3” or “4”. Fuel only. |
| Field | Type | Description |
|---|---|---|
FleetGrossNonFuelPriceoptional | H163 · n12 · 2dp implied | Gross 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. |
FleetNetNonFuelPriceoptional | H164 · n12 · 2dp implied | Net non-fuel price — Optional, exclusive of taxes. Applies only when TypeOfPurchase is “2” or “3”. Non-fuel only. |
| Field | Type | Description |
|---|---|---|
FleetOdometerReadingrequired to qualify | H165 · n7 | Odometer — Whole miles. Digits only - no commas, no decimals. |
VisaFleetEmpNoauto-derived | S26 · an12 | Employee number — When the card prompts for it. Defaults to 1. |
VisaFleetTrlrNorequired to qualify | S27 · an16 | Trailer number — When the card prompts for it. |
VisaFleetAddProptData1required to qualify | S28 · an20 | Prompted data 1 |
VisaFleetAddProptData2required to qualify | S29 · an20 | Prompted 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.
// 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"
}
}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.
// 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"
}
}Surcharge / Tip / Cashback
Surcharge, tip, and cashback amounts ride in the AdditionalAmounts field (DE 54). Two rules:
ProcessingCodestays"000000"for surcharge and tip. ONLY a cash-back request flips the first two digits to"09"– sending090000on a credit surcharge sale is a format-error decline (code 30).TransactionAmountis the total – base + surcharge + tip + cashback.
| Field | Type | Description |
|---|---|---|
ProcessingCoderequired | string | '000000' for surcharge/tip; '090000' ONLY when requesting cash back (debit/EBT). |
TransactionAmountrequired | string | The total: base + surcharge + tip + cashback. 12-digit cents. |
AdditionalAmountsrequired | string | 20-character positional string (DE 54). Layout below. |
AdditionalAmounts is a 20-character positional string:
| Pos | Field | Value |
|---|---|---|
| 1-2 | Account type | "00" default account |
| 3-4 | Amount type | "42" surcharge · "43" tip · "40" cashback · "39" cumulative incremental-auth amount |
| 5-7 | Currency code | "840" (USD) |
| 8 | Credit / debit | "C" credit · "D" debit |
| 9-20 | Amount (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.
// 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",
...
}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.AdditionalAmountsamount type"39"carries the cumulative authorized total;TransactionAmountis the additional amount being authorized in this message.- The original
RetrievalReferenceNumber(DE 37) must be present and match – response code98“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.
// 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.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.
| Field | Type | Description |
|---|---|---|
RequestDataElements.CVV2RequestValueoptional | string | Fixed 6-character control field: presence flag + response-request flag + the code, left-justified and space-padded. Layout below. |
| Position | Meaning | Values |
|---|---|---|
| 1 | Presence | 1 = code was sent · 0 = not provided |
| 2 | Response request | 1 = return the response code and the CVV2 result code |
| 3–6 | The code | 3-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):
| Code | Meaning |
|---|---|
| M | Match |
| N | No match |
| P | Not processed |
| S | Merchant flagged the code as not present, but it should be on the card |
| U | Issuer 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 type | Name | Length | Normalized result |
|---|---|---|---|
| Visa | CVV2 | 3 | M / N / P / S / U |
| Mastercard | CVC2 | 3 | M / N / P / S / U |
| Discover | CID | 3 | M / N / P / S / U |
| American Express | CID | 4 | M / N / P / S / U |
// 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" }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.
| Field | Type | Description |
|---|---|---|
RequestDataElements.ZipCodeoptional | string | Cardholder billing ZIP. AVS runs whenever this is present. |
RequestDataElements.Addressoptional | string | Cardholder billing street. AVS runs whenever this is present. |
| Code | Meaning |
|---|---|
| M | Address + ZIP match |
| Y | Address + ZIP (5-digit) match |
| X | Address + ZIP (9-digit) match |
| A | Address matches, ZIP does not |
| Z | ZIP matches, address does not |
| W | ZIP (9-digit) matches, address does not |
| P | Postal matches, street not verified |
| B | Street matches (intl), postal not verified |
| D | Address + ZIP match (intl) |
| N | No match – neither address nor ZIP |
| I | Address not verified (intl) |
| G | Non-U.S. issuer, not verified |
| R | Retry – issuer system unavailable |
| S | AVS not supported by issuer |
| U | Address information unavailable |
| E | AVS error / not allowed for this transaction |
// 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": "..."
}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.
/hostapi/settlementrequest/Reconcile – post your tallied batch totals. MTI 500, ProcessingCode 920000. 00 = settled; 95 = mismatch./hostapi/batchupload/On a 95: upload each captured transaction, one POST per capture. MTI 320, same BatchNumber./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 95 – totals mismatch. Cygma's tape and yours disagree, so it needs the itemized batch to reconcile.
On a 95, run the two-step upload flow:
- Post one
/hostapi/batchupload/(MTI 320) for each captured transaction in the batch – the sameBatchNumber, echoing the originalSystemsTraceNumber,ProcessingCode,TransactionAmount, andAuthorizationIdResponse. - 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 returns00and the batch settles.
The totals block is four count / amount pairs. Amounts are 12-digit, zero-padded, in minor units (cents):
| Field | Type | Description |
|---|---|---|
RequestDataElements.CreditCardSalesCountrequired | string | Number of credit sales in the batch. |
RequestDataElements.CreditCardSalesAmountrequired | string | Total credit sales, 12-digit cents. |
RequestDataElements.CreditCardRefundCountrequired | string | Number of credit refunds. |
RequestDataElements.CreditCardRefundAmountrequired | string | Total credit refunds, 12-digit cents. |
RequestDataElements.DebitCardSalesCountoptional | string | Number of debit sales. |
RequestDataElements.DebitCardSalesAmountoptional | string | Total debit sales, 12-digit cents. |
RequestDataElements.DebitCardRefundCountoptional | string | Number of debit refunds. |
RequestDataElements.DebitCardRefundAmountoptional | string | Total debit refunds, 12-digit cents. |
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"
}
}'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.
// "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
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:
010manual / key-entered (default)021Track 2 read022Track 1 read050EMV chip070contactless090EMV 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).
| Field | Type | Description |
|---|---|---|
Track2Dataoptional | string | Raw track 2 data from a swipe (or the chip's track-2 image on an EMV read). Omit PrimaryAccountNumber when this is sent. |
Track1Dataoptional | string | Raw track 1 data (DE 45; less common – some MSRs send both). |
ICCSystemRelatedDataoptional | string | DE 55 – the EMV ICC TLV payload (hex) from the chip kernel. Required for chip / contactless reads. |
RequestDataElements.KeySerialNumberoptional | string | 3DES DUKPT Key Serial Number when the capture device encrypts (P2PE). |
POSEntryModerequired | string | Set to match the read type: '021' track 2, '022' track 1, '050' EMV chip, '070' contactless, '090' EMV fallback. |
// 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",
...
},
...
}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.
# 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)