Integration Guide

This guide explains how to send airline booking data (Level-3 airline data) with your N-Genius online payments, and how to settle a booking in several partial captures (multi-clearing). It covers authentication, endpoints, payloads, validation rules, samples, refunds, voids and error handling.

🚧

Before you start

Airline Acquiring must be enabled on your outlet. Contact your Network International relationship manager to enable it. If it isn't enabled, the gateway accepts your orders but ignores the airline data. See Errors and exceptions.

Integration overview

sequenceDiagram
    participant M as Merchant server
    participant N as N-Genius API
    participant C as Customer
    M->>N: POST /identity/auth/access-token (API key)
    N-->>M: access_token
    M->>N: POST /transactions/outlets/[outlet]/orders<br/>action AUTH + airlineDetails
    N-->>M: 201 order (orderRef, paymentRef, payment page link)
    M->>C: Redirect to payment page (or collect card via direct API)
    C->>N: Enters card details
    N-->>M: Payment AUTHORISED
    loop For each passenger / clearing
        M->>N: POST .../payments/[paymentRef]/captures<br/>amount + airline (seq, count, M/F)
        N-->>M: 201 PARTIALLY_CAPTURED (or CAPTURED)
    end
    opt After the event
        M->>N: Refund a capture / void
    end

Which order action should I use?

ActionWhen to useMulti-clearing captures
AUTHYou settle the booking in one or more captures later. Choose this for multi-passenger or multi-clearing bookings.Yes
PURCHASEYou charge and settle the whole amount at once.No: the payment is captured automatically, so there is no capture step

Airline details on the order are used for the authorisation, the sale (purchase) and every capture. You don't need to resend them for refunds, voids or reversals.

Environments and authentication

Base URLs

EnvironmentBase URL
Sandboxhttps://api-gateway.sandbox.ngenius-payments.com
Productionhttps://api-gateway.ngenius-payments.com

Get an access token

Every API call needs a short-lived access token. Request one with your API key, which you can find in the N-Genius portal under Settings → Integrations → Service Accounts.

POST {baseUrl}/identity/auth/access-token
HeaderValue
AuthorizationBasic {your_api_key}
Content-Typeapplication/vnd.ni-identity.v1+json
Acceptapplication/vnd.ni-identity.v1+json

Sample request

curl --location --request POST '{baseUrl}/identity/auth/access-token' \
--header 'Authorization: Basic {your_api_key}' \
--header 'Content-Type: application/vnd.ni-identity.v1+json' \
--header 'Accept: application/vnd.ni-identity.v1+json'

Sample response: 200 OK

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI...",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "token_type": "bearer"
}
  • Send the token as Authorization: Bearer {access_token} on every transaction call.

  • The token is short-lived (expires_in is in seconds). Request a new one when it expires.

  • Keep your API key and tokens on your server only. Never put them in a browser or mobile app.

Common headers

All transaction endpoints in this guide use:

HeaderValueRequired
AuthorizationBearer {access_token}Yes
Content-Typeapplication/vnd.ni-payment.v2+jsonYes, on requests with a body
Acceptapplication/vnd.ni-payment.v2+jsonYes

API endpoints

{outletRef} is your outlet reference, which is in the N-Genius portal. {orderRef}, {paymentRef}, {captureId}, {purchaseId} and {refundId} come from earlier responses.

#OperationMethodPath
1Get access tokenPOST/identity/auth/access-token
2Create order (with airlineDetails)POST/transactions/outlets/{outletRef}/orders
3Get orderGET/transactions/outlets/{outletRef}/orders/{orderRef}
4Pay with card (direct API)PUT/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/card
5Capture (with airline)POST/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures
6Get captureGET/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures/{captureId}
7Void a captureDELETE/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures/{captureId}
8Refund a capturePOST/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures/{captureId}/refund
9Refund a purchasePOST/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/purchases/{purchaseId}/refund
10Get refundGET/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/refund/{refundId}
11Void a refundDELETE/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/refund/{refundId}
12Void (reverse) an authorisationPUT/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/cancel

What's new for airline: operation 2 accepts airlineDetails, and operation 5 accepts airline. All other operations are standard N-Genius operations, unchanged.

📘

Use the response links

Follow the _links in each response (for example payment:card, cnp:capture, cnp:refund, cnp:cancel) instead of building URLs by hand where you can.

Create an order with airline details

POST {baseUrl}/transactions/outlets/{outletRef}/orders

Send the standard order fields (action, amount, and so on) plus the airlineDetails object. Every field inside airlineDetails is optional. Send what you have, and each field you send must pass its validation rule (Field reference).

Restrictions

  • Allowed only for single (one-time) orders. Recurring and subscription orders are rejected.

  • When airlineDetails is present, only card payment methods are offered. Alternative payment methods are hidden.

Sample request

curl --location '{baseUrl}/transactions/outlets/{outletRef}/orders' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/vnd.ni-payment.v2+json' \
--header 'Accept: application/vnd.ni-payment.v2+json' \
--data-raw '{
  "action": "AUTH",
  "amount": { "currencyCode": "AED", "value": 300000 },
  "language": "en",
  "emailAddress": "[email protected]",
  "merchantAttributes": {
    "redirectUrl": "https://merchant.example.com/payment-result"
  },
  "airlineDetails": {
    "bookingReferenceNumber": "BRN000000006",
    "documentType": "PASSENGER_TICKET",
    "transactionType": "TICKET_PURCHASE",
    "planNumber": "01",
    "itinerary": {
      "numberInParty": 2,
      "originCountry": "ARE",
      "legs": [
        {
          "carrierCode": "EK",
          "customerCode": "CUST12345",
          "couponNumber": "1",
          "departureAirport": "DXB",
          "departureDate": "2026-07-15",
          "departureTime": "09:30:00+04:00",
          "departureTax": "15.00",
          "destinationAirport": "LHR",
          "destinationArrivalDate": "2026-07-15",
          "destinationArrivalTime": "13:45:00+01:00",
          "endorsementsRestrictions": "NONREF",
          "fare": "3500.00",
          "fareBasis": "JOWBH",
          "fees": "120.00",
          "flightNumber": "EK001",
          "stopoverPermitted": false,
          "taxes": "230.00",
          "travelClass": "J"
        }
      ]
    },
    "passengers": [
      {
        "title": "MR",
        "firstName": "Joe",
        "middleName": "Michael",
        "lastName": "Bloggs",
        "frequentFlyerNumber": "EK1234567890",
        "specificInformation": "VIP001"
      },
      {
        "title": "MS",
        "firstName": "Jane",
        "middleName": "Anne",
        "lastName": "Smith",
        "frequentFlyerNumber": "EK1234567891",
        "specificInformation": "VIP002"
      }
    ],
    "ticket": {
      "ticketNumber": "1761234567890",
      "eTicket": true,
      "restricted": false,
      "conjunctionTicketIndicator": false,
      "issue": {
        "address": "Terminal 3",
        "carrierCode": "EK",
        "carrierName": "Emirates",
        "city": "Dubai",
        "country": "ARE",
        "date": "2026-06-22",
        "travelAgentCode": "23456789",
        "travelAgentName": "Global Travel LLC"
      },
      "taxOrFees": [
        { "amount": "200.00", "type": "YQT" },
        { "amount": "30.00",  "type": "UKS" }
      ],
      "totalFare": "3500.00",
      "totalFees": "120.00",
      "totalTaxes": "230.00"
    }
  }
}'

Sample response: 201 Created

The response echoes airlineDetails. Save reference (orderRef) and _embedded.payment[0].reference (paymentRef).

{
  "_id": "urn:order:2c4b0a2e-6f1d-4d8b-9b3e-0d9f1b7a6c11",
  "_links": {
    "self":    { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b0a2e-6f1d-4d8b-9b3e-0d9f1b7a6c11" },
    "payment": { "href": "https://paypage.sandbox.ngenius-payments.com/?code=8a1f3c9e2b7d4e10" }
  },
  "type": "SINGLE",
  "action": "AUTH",
  "amount": { "currencyCode": "AED", "value": 300000 },
  "language": "en",
  "merchantAttributes": { "redirectUrl": "https://merchant.example.com/payment-result" },
  "emailAddress": "[email protected]",
  "reference": "2c4b0a2e-6f1d-4d8b-9b3e-0d9f1b7a6c11",
  "outletId": "{outletRef}",
  "createDateTime": "2026-06-22T08:15:30.123Z",
  "paymentMethods": { "card": ["VISA", "MASTERCARD"] },
  "airlineDetails": { "...": "same object you sent" },
  "_embedded": {
    "payment": [
      {
        "_id": "urn:payment:7d3e5f10-2a4b-4c6d-8e9f-a1b2c3d4e5f6",
        "_links": {
          "self":         { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e..." },
          "payment:card": { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e.../card" }
        },
        "reference": "7d3e5f10-2a4b-4c6d-8e9f-a1b2c3d4e5f6",
        "state": "STARTED",
        "amount": { "currencyCode": "AED", "value": 300000 },
        "orderReference": "2c4b0a2e-6f1d-4d8b-9b3e-0d9f1b7a6c11"
      }
    ]
  }
}
👍

Tip

If airlineDetails is missing from the response, Airline Acquiring isn't enabled on the outlet and the data was dropped. See Errors and exceptions.

Take the card payment

Use either option:

OptionHowPCI scope
Hosted payment page (recommended)Redirect the customer to _links.payment.href from the order response. The customer returns to your redirectUrl.Lowest
Direct APIPUT the card details to _embedded.payment[0]._links["payment:card"].hrefRequires PCI DSS compliance

Direct API sample

curl --location --request PUT '{payment:card href}' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/vnd.ni-payment.v2+json' \
--header 'Accept: application/vnd.ni-payment.v2+json' \
--data '{
  "pan": "4111111111111111",
  "expiry": "2030-12",
  "cvv": "123",
  "cardholderName": "Joe Bloggs"
}'

The result is AUTHORISED (for AUTH) or PURCHASED (for PURCHASE). If the state is AWAIT_3DS, complete 3-D Secure first, as in a standard N-Genius integration. Always confirm the final state with Get order (GET .../orders/{orderRef}) before you ship the ticket.

Capture with multi-clearing data

POST {baseUrl}/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures

Use this on an AUTH order to settle the authorised amount in several partial captures, for example one capture per passenger or per ticket. Add the airline object to each capture to describe its position in the set.

Request body

FieldMandatoryDescription
amount.currencyCodeYesSame currency as the order
amount.valueYesAmount for this capture, in minor units (e.g. 100000 = AED 1,000.00)
airline.multiClrSeqNumYesPosition of this capture in the set, two digits: 01, 02, …
airline.multiClrCntNumYesTotal number of captures in the set, two digits, e.g. 03
airline.multiClrIndYesM = more captures will follow, F = this is the final capture
airline.ticketNumberYesTicket number for this capture. If left out, the order's ticket number is used
airline.passengerFirstNameYesPassenger first name for this capture. If left out, the first passenger on the order is used
airline.passengerMiddleNameYesPassenger middle name for this capture. Same default as first name
airline.passengerLastNameYesPassenger last name for this capture. Same default as first name

Rules

  • The airline block is used only for partial captures, where the capture amount is less than the amount still authorised. On a full capture it is ignored.

  • Leave out optional fields you don't need. Don't send them as empty strings (""), because the capture is rejected.

  • Each optional field is applied independently. For example, you can send only ticketNumber.

  • Keep multiClrCntNum the same across the set, increase multiClrSeqNum by one each time, and send F only on the last capture.

Sample request: capture 1 of 2 (first passenger)

curl --location '{baseUrl}/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/vnd.ni-payment.v2+json' \
--header 'Accept: application/vnd.ni-payment.v2+json' \
--data '{
  "amount": { "currencyCode": "AED", "value": 150000 },
  "airline": {
    "multiClrSeqNum": "01",
    "multiClrCntNum": "02",
    "multiClrInd": "M",
    "ticketNumber": "1761234567890",
    "passengerFirstName": "Joe",
    "passengerMiddleName": "Michael",
    "passengerLastName": "Bloggs"
  }
}'

Sample request: capture 2 of 2 (second passenger, final)

{
  "amount": { "currencyCode": "AED", "value": 150000 },
  "airline": {
    "multiClrSeqNum": "02",
    "multiClrCntNum": "02",
    "multiClrInd": "F",
    "ticketNumber": "1761234567891",
    "passengerFirstName": "Jane",
    "passengerMiddleName": "Anne",
    "passengerLastName": "Smith"
  }
}

Sample response: 201 Created

The response is the payment. Each capture appears under _embedded["cnp:capture"]. Save the capture id from its self link if you might refund or void it.

{
  "_id": "urn:payment:7d3e5f10-2a4b-4c6d-8e9f-a1b2c3d4e5f6",
  "_links": {
    "self":        { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e..." },
    "cnp:capture": { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e.../captures" },
    "cnp:cancel":  { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e.../cancel" }
  },
  "reference": "7d3e5f10-2a4b-4c6d-8e9f-a1b2c3d4e5f6",
  "state": "PARTIALLY_CAPTURED",
  "amount": { "currencyCode": "AED", "value": 300000 },
  "orderReference": "2c4b0a2e-6f1d-4d8b-9b3e-0d9f1b7a6c11",
  "_embedded": {
    "cnp:capture": [
      {
        "_links": {
          "self":       { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e.../captures/5b6c7d8e-..." },
          "cnp:refund": { "href": "{baseUrl}/transactions/outlets/{outletRef}/orders/2c4b.../payments/7d3e.../captures/5b6c7d8e-.../refund" }
        },
        "amount": { "currencyCode": "AED", "value": 150000 },
        "createdTime": "2026-06-23T10:02:11.456Z",
        "state": "SUCCESS"
      }
    ]
  }
}

After the final capture, the payment state becomes CAPTURED once the full amount is captured. Otherwise it stays PARTIALLY_CAPTURED.

Refunds, voids and reversals

You don't send airline data on any of these operations. The gateway uses the original transaction.

ScenarioWhenCallBody
Reverse an authorisation (release funds)AUTH order, nothing captured yet (AUTHORISED)PUT .../payments/{paymentRef}/cancelnone
Void a captureSame day, before settlementDELETE .../payments/{paymentRef}/captures/{captureId}none
Refund a captureAfter settlement, per capture (e.g. one passenger cancels)POST .../payments/{paymentRef}/captures/{captureId}/refund{ "amount": { "currencyCode": "AED", "value": 150000 } }
Refund a purchasePURCHASE orderPOST .../payments/{paymentRef}/purchases/{purchaseId}/refund{ "amount": { ... } }
Void a refundBefore the refund settlesDELETE .../payments/{paymentRef}/refund/{refundId}none
  • Refunds can be partial: refund less than the capture amount, for example one fee only.

  • The total refunded can't exceed what was captured.

  • Use the cnp:cancel and cnp:refund links in the responses. The gateway only returns a link when that action is currently allowed.

Sample: refund one passenger's capture

curl --location '{baseUrl}/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/captures/{captureId}/refund' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/vnd.ni-payment.v2+json' \
--header 'Accept: application/vnd.ni-payment.v2+json' \
--data '{ "amount": { "currencyCode": "AED", "value": 150000 } }'

The response is the payment with the refund under _embedded["cnp:refund"]. The payment state becomes PARTIALLY_REFUNDED or REFUNDED.

Sample: reverse an authorisation

curl --location --request PUT '{baseUrl}/transactions/outlets/{outletRef}/orders/{orderRef}/payments/{paymentRef}/cancel' \
--header 'Authorization: Bearer {access_token}' \
--header 'Content-Type: application/vnd.ni-payment.v2+json' \
--header 'Accept: application/vnd.ni-payment.v2+json'

The payment state becomes REVERSED.

Integration flow examples

Two passengers, settled per passenger

StepCallKey dataPayment state after
1Get tokenAPI keyn/a
2Create orderaction: AUTH, amount: 300000, airlineDetails with 2 passengersSTARTED
3Customer payshosted page / direct cardAUTHORISED
4Capture 1150000, 01 / 02 / M, Joe Bloggs, ticket …890PARTIALLY_CAPTURED
5Capture 2150000, 02 / 02 / F, Jane Smith, ticket …891CAPTURED

One passenger cancels after settlement

StepCallKey dataPayment state after
1–5As in the first exampleCAPTURED
6Refund capture 2POST .../captures/{capture2Id}/refund, 150000PARTIALLY_REFUNDED

Booking cancelled before any capture

StepCallKey dataPayment state after
1–3As in the first exampleAUTHORISED
4Reverse authorisationPUT .../payments/{paymentRef}/cancelREVERSED

Single passenger, charge immediately

StepCallKey dataPayment state after
1Get tokenn/a
2Create orderaction: PURCHASE, airlineDetailsSTARTED
3Customer paysPURCHASED
4(Optional) refundPOST .../purchases/{purchaseId}/refundPARTIALLY_REFUNDED / REFUNDED

Postman collection

Two files are provided:

FileContents
postman/Airline-Payments.postman_collection.jsonAll requests in this guide, in flow order, plus negative tests. Test scripts save accessToken, orderRef, paymentRef, captureId, purchaseId and refundId automatically
postman/Airline-Payments-Sandbox.postman_environment.jsonVariables: baseUrl, apiKey, outletRef (fill in apiKey and outletRef)

How to use

  1. In Postman, choose Import and import both files.

  2. Select the Airline Payments – Sandbox environment, then set apiKey and outletRef.

  3. Run 01 Get access token, then 02 Create AUTH order with airline details.

  4. Open the paymentPageUrl variable in a browser and pay with a sandbox test card, or run 03 Pay with card (direct API) if your outlet allows it.

  5. Run the captures, refunds and voids in folder order.

📘

Collection format

Postman v2.1. It can be imported by all current Postman versions.

© Network International LLC. All Rights Reserved.