Payment Orchestrator
Basket Items

Basket Items

Basket items (basketItems[]) describe, line by line, what a payment covers. This page collects the item fields, the rules and the optional insurance fields you can add to an item in one place.

Amounts are integers in minor units: "price": 10050 = ₺100.50.

Endpoints that take items

EndpointItemInsurance fields
POST /payments/intents (Checkout Form)basketItems[]Accepted
POST /payments/direct/payment (Payment)basketItems[]Not accepted; the request is rejected with 400 if sent
POST /payments/marketplace/intents, /marketplace/2d, /marketplace/3dbasketItems[]Not accepted; the request is rejected with 400 if sent
POST /payments/links, PATCH /payments/links/{id} (Payment Links)items[], in its own formatAccepted, except insuranceContext and insuranceData (see below)

Item fields

FieldTypeRequiredRule
idstringYesMust not be empty. Must be unique within the basket when any item carries insurance fields.
priceintegerYesMinor units, at least 0. The total amount of the line.
namestringYesMust not be empty.
category1stringYesMust not be empty.
category2stringNo—
itemTypeenumYesPHYSICAL or VIRTUAL only.

Rules:

  • An item has no quantity field; price is the line total. If you sell 3 units of the same product, send either one item with the total of the 3 units or a separate item per unit. Items that carry insurance fields are one item per unit (see below).
  • In every payment request that takes items, the sum of the items' price must equal the request's amount; otherwise the request is rejected with 400 VALIDATION_ERROR. amount excludes the installment fee; for installments the system adds the fee to the charged amount, not to the items.
  • A field that is not defined is rejected. Send insurance fields only to the endpoints that accept them.

Validation errors

An error in an item returns 400 VALIDATION_ERROR. The field in details.errors[] gives the failing field as a dotted path that includes the item's position. Positions start at 0: basketItems.0.itemType is the itemType of the first item.

{
  "status": "failure",
  "code": "VALIDATION_ERROR",
  "message": "Validation failed — check the submitted fields.",
  "statusCode": 400,
  "requestId": "4b0e6c1a-2f3d-4e5a-9b8c-7d6e5f4a3b2c",
  "details": {
    "errors": [
      { "field": "basketItems", "errors": [] },
      { "field": "basketItems.0.itemType", "errors": ["itemType must be one of the following values: PHYSICAL, VIRTUAL"] }
    ]
  }
}
  • The basketItems entry always comes first; the reasons for item errors are in the path entries that follow it.
  • The path grows for nested fields: basketItems.0.insuranceContext.departureAt.
  • An unknown field is reported the same way: basketItems.1.pnr.
  • Branch in your code on code and field, not on message. The error envelope: Errors.

Insurance fields

⚠️

The insurance fields are accepted live and do not affect your payment; if insurance is not enabled on your account, they are not used.

Which insurance your buyer is offered is resolved from the insurance fields of your items. You can also send the information the quote needs together with the item; the buyer is then not asked for it again. How the product works: Insurance.

  • All insurance fields are optional. If you do not send them, your payment works exactly as it does today.
  • Insurance fields are not passed to your payment provider.
  • If insurance cannot be offered, the payment does not stop; insurance is simply not shown.
  • Insurance is never added on its own; the buyer must explicitly accept the quote.
FieldTypeRuleMeaning
insuranceCategoryenumA value from the item category dictionaryThe item's category. The eligible insurance types are resolved from this field.
insuranceTypesstring[]Each element ^[A-Z][A-Z0-9_]{2,63}$; unique; at most 10 elementsInsurance type codes; narrows the eligible types. If omitted or empty, all types of the item category.
insuranceEligiblebooleantrue or falseIf false, no insurance is offered for this item; takes precedence over the other insurance fields.
insuranceValueAmountintegerMinor units, at least 1The insured value of one unit. If omitted, the unit price is used.
insuranceContextobjectAllowed keys only; an unknown key is rejectedNon-personal details (trip, device model, travel dates and so on). Pre-fills the fields of the insurance type: Pre-filling.
insuranceDataobjectInsurance pre-fill data; at most 16 KBThe other details the quote needs, personal data included (insured person, IMEI, booking code, plate and so on). May be partial.

Rules:

  • If you send insuranceTypes, insuranceValueAmount, insuranceContext or insuranceData, insuranceCategory is required. insuranceEligible: false can be sent on its own.
  • If at least one item carries insurance fields, the ids in the basket must be unique. The rule does not apply to a basket without insurance fields.
  • Insurance is per unit. Each unit is insured separately and the sum insured is the unit price. So in an item that carries insurance fields, send each unit as a separate item: 3 tickets, 3 items.
  • The insurance premium item (the item whose id starts with inso_) carries no insurance fields; if it does, the request is rejected with 400 VALIDATION_ERROR.
  • Only the format is validated during the request. Whether the type code is recognised, whether it fits the item category and whether it is enabled on your account are checked later; a mismatch does not stop the payment, insurance is simply not offered.

Item category dictionary

ValueItem
TICKETBus, flight or train ticket
HOTELAccommodation booking
EVENTEvent ticket
DEVICEMobile phone
ELECTRONICSElectronics, home appliances
VEHICLEVehicle
TRAVELTravel, tour package
HOMEHome
HEALTHHealth service
PETPet
MICROMOBILITYRented scooter and similar micromobility
MUSICAL_INSTRUMENTMusical instrument
SHIPMENTFreight, parcel shipment
EDUCATIONEducation, study abroad

New values may be added to the dictionary; a published value never changes and is never removed. The Lunixi catalogue determines which insurance types an item category maps to. Current mappings: TICKET → TICKET_CANCELLATION, HOTEL → HOTEL_CANCELLATION, DEVICE → DEVICE_INSURANCE and DEVICE_PROTECTION, ELECTRONICS → EXTENDED_WARRANTY, VEHICLE → ROADSIDE_ASSISTANCE, VEHICLE_TAG_PROTECTION, MOTOR_GREEN_CARD, MOTOR_THIRD_PARTY_LIABILITY, MOTOR_COMPREHENSIVE and MOTOR_EXCESS_LIABILITY, TRAVEL → TRAVEL_HEALTH, INBOUND_TRAVEL_HEALTH, MOTOR_GREEN_CARD and ABROAD_STAY_HEALTH, EVENT → EVENT_ACCIDENT, MICROMOBILITY → MICROMOBILITY_ACCIDENT, MUSICAL_INSTRUMENT → MUSICAL_INSTRUMENT_PROTECTION, HEALTH → MEDICAL_COMPLICATION, PET → PET_HEALTH, EDUCATION → ABROAD_STAY_HEALTH, HOME → COMPULSORY_EARTHQUAKE and HOME_INSURANCE. If an item category maps to several types, you can narrow the types offered with insuranceTypes. No insurance is offered for an item category without a mapping. Types not linked to an item category are sold without a basket item, on the API path only: Selling Without a Basket Item. For the types see Insurance Types.

insuranceContext

The context carries the non-personal fields of the insurance type. The keys are the field names in the type object. The allowed keys are listed below by item category.

TICKET:

KeyFormat
transportModeBUS, FLIGHT or TRAIN
carrierText, 1-100 characters
departureAtTimestamp, time zone required (2025-12-23T12:50:00+03:00)
originCityText, 1-100 characters
destinationCityText, 1-100 characters
originAirportCodeIATA, three upper-case letters
destinationAirportCodeIATA, three upper-case letters
flightNumberText, 1-100 characters
isPromotionaltrue or false

HOTEL:

KeyFormat
hotelNameText, 1-200 characters
checkInDateDate, YYYY-MM-DD
checkOutDateDate, YYYY-MM-DD
guestCountInteger, at least 1
cityCodeProvince plate code, 01-81

DEVICE:

KeyFormat
brandCodeLunixi reference code
modelCodeLunixi reference code
coverageStartDateDate, YYYY-MM-DD

ELECTRONICS:

KeyFormat
categoryCodeLunixi reference code
brandText, 1-100 characters
modelText, 1-100 characters
warrantyMonths12 or 24 (number)

VEHICLE:

KeyFormat
brandCodeLunixi reference code
modelCodeLunixi reference code
modelYearInteger
vehicleTypeCodeLunixi reference code
vehicleUsageCodeLunixi reference code (usage)
seatCountInteger, number of seats including the driver

TRAVEL:

KeyFormat
scopeDOMESTIC or INTERNATIONAL
startDateDate, YYYY-MM-DD
endDateDate, YYYY-MM-DD
destinationCountryCodesArray; each entry ISO 3166-1 alpha-2, at most 20 entries

EVENT and MICROMOBILITY:

KeyFormat
coverageStartDateDate, YYYY-MM-DD

MUSICAL_INSTRUMENT:

KeyFormat
categoryCodeLunixi reference code
brandText, 1-100 characters
modelText, 1-100 characters

HEALTH:

KeyFormat
procedureCoverCodeLunixi reference code
procedureDateDate, YYYY-MM-DD
facilityNameText, 1-200 characters

PET:

KeyFormat
petSpeciesCAT or DOG
petBreedCodeLunixi reference code

EDUCATION:

KeyFormat
startDateDate, YYYY-MM-DD
endDateDate, YYYY-MM-DD

HOME:

KeyFormat
areaSqmInteger, gross area (square metres), 1-100000
constructionYearInteger, the building's construction year
dwellingTypeCodeLunixi reference code (dwelling type)

SHIPMENT:

KeyFormat
originCityCodeOrigin province, plate code 01-81
destinationCityCodeDestination province, plate code 01-81
departureDateDate, YYYY-MM-DD

Keys are shared across item categories: the tables show the category a key is used with most. The payment request does not check whether a key fits the item category; a key is used for pre-filling when the item's insurance type has a matching field (Pre-filling). The values are validated against the type's schema (for example, the check-out day being after the check-in day, or the model year range) at quote time: Insurance Types.

  • insuranceContext carries no personal data. If the booking code (pnr), the ticket number (ticketNumber), the IMEI (imei), the plate (plate), the UAVT address code (uavtAddressCode), the serial number (serialNumber), the country of residence (residenceCountryCode), the nationality (nationalityCountryCode) or person fields (such as identityNumber, firstName, birthDate) are sent here, the request is rejected. To send these details, use the insuranceData field.
  • Amount fields are not sent here; the insured value is in insuranceValueAmount. You can give the type's amount field (for example the ticket amount priceAmount) in insuranceData.
  • If there is no value, do not send the key. Empty text or placeholder values such as "yok" ("none") are not accepted.
  • Cross-field rules, such as airport codes being valid only for flight tickets, are checked at quote time.

Details of timestamp, date and code formats: Data Formats.

Pre-filling

A context key fills the field with the same name in the type object; the value is used as is. In the Checkout Form these fields arrive filled in and the buyer can change them. In the API they are returned in the prefill field of the Eligibility response and complete the fields you leave out of the quote request.

Insurance typeType objectFields filled from the context
TICKET_CANCELLATIONticketstransportMode, carrier, departureAt, originCity, destinationCity, originAirportCode, destinationAirportCode, flightNumber, isPromotional
HOTEL_CANCELLATIONstayhotelName, checkInDate, checkOutDate, guestCount, cityCode
DEVICE_INSURANCE, DEVICE_PROTECTIONdevicebrandCode, modelCode, coverageStartDate
EXTENDED_WARRANTYproductcategoryCode, brand, model, warrantyMonths
ROADSIDE_ASSISTANCEvehiclebrandCode, modelCode, modelYear, vehicleTypeCode
VEHICLE_TAG_PROTECTIONvehiclecoverageStartDate
MOTOR_GREEN_CARDvehicleseatCount, vehicleTypeCode, startDate, endDate
MOTOR_THIRD_PARTY_LIABILITYvehiclemodelCode, modelYear, vehicleTypeCode
MOTOR_COMPREHENSIVEvehiclemodelCode, modelYear, vehicleTypeCode, coverageStartDate
MOTOR_EXCESS_LIABILITYvehiclemodelCode, modelYear, vehicleTypeCode, vehicleUsageCode, coverageStartDate
TRAVEL_HEALTHtripscope, startDate, endDate, destinationCountryCodes, originCityCode
INBOUND_TRAVEL_HEALTHtripstartDate, endDate
ABROAD_STAY_HEALTHabroadStaystartDate, endDate
MICROMOBILITY_ACCIDENTrentalcoverageStartDate
MUSICAL_INSTRUMENT_PROTECTIONinstrumentbrand, model, categoryCode
EVENT_ACCIDENTeventcoverageStartDate
MEDICAL_COMPLICATIONprocedureprocedureCoverCode, procedureDate, facilityName, startDate, endDate
PET_HEALTHpetcoverageStartDate, petSpecies, petBreedCode
COMPULSORY_EARTHQUAKEpropertycoverageStartDate, areaSqm, constructionYear
HOME_INSURANCEpropertyareaSqm, constructionYear, dwellingTypeCode
  • A key that is not in the table is not used for that type; for example warrantyMonths for DEVICE_PROTECTION.
  • A context value that does not fit the type's schema, or does not apply in that case, is not used for pre-filling and does not block the quote. For example, an airport code in the context of a ticket with transportMode: BUS is ignored.
  • If the same field is also in insuranceData, the value in insuranceData applies. A value sent in the quote request (entered by the buyer in the form, or sent by you in the API) takes precedence over both.

Insurance pre-fill data: insuranceData

You can send the information the quote needs, personal data included, together with the item to be insured: the insured person, the device's IMEI, the booking code, the vehicle's plate and so on. Depending on how much you send:

What you sendResult
NothingAs today: the buyer fills in the details in the form, or you send them in the quote request.
Part of the detailsThe fields you sent are ready; the buyer is asked only for the missing ones, or you send only the missing ones in the quote request.
Everything the quote needsThe quote is obtained without further details. In the Checkout Form the quote arrives on its own when the form opens, if the policy fields arrived too.

In every case insurance is added only with the buyer's explicit acceptance. Behaviour in the form and in the API: Insurance › Basket details and pre-fill data.

Format

  • The keys are the root fields of the insurance type's quote request: policyholder, insured and the type object. Field names and formats are the same as on the Insurance Types and Schemas page; there is no separate dictionary.
  • The type object is a single object for the item. tickets, which is an array in the quote request, is also given here as a single object: "tickets": { "pnr": "…" }.
  • basketItemId is not sent in the type object; the item's id is used. If it is sent, the request is rejected.
  • insured is an array of objects: 1-10 elements.
  • Partial data is allowed. Required fields are not checked here; the buyer fills in the missing fields in the form, or you send them in the quote request.
  • If there is no value, do not send the field. null values and an empty object ({}) are not accepted.
  • At most 16 KB as JSON (UTF-8).
RootFormatTypes that use it
policyholderobjectThe policyholder; person fields
insuredarray, 1-10 objectsThe insured persons; person fields
ticketsobjectTICKET_CANCELLATION
stayobjectHOTEL_CANCELLATION
deviceobjectDEVICE_INSURANCE, DEVICE_PROTECTION
productobjectEXTENDED_WARRANTY
vehicleobjectROADSIDE_ASSISTANCE, VEHICLE_TAG_PROTECTION, MOTOR_GREEN_CARD, MOTOR_THIRD_PARTY_LIABILITY, MOTOR_COMPREHENSIVE, MOTOR_EXCESS_LIABILITY
tripobjectTRAVEL_HEALTH, INBOUND_TRAVEL_HEALTH
rentalobjectMICROMOBILITY_ACCIDENT
instrumentobjectMUSICAL_INSTRUMENT_PROTECTION
eventobjectEVENT_ACCIDENT
procedureobjectMEDICAL_COMPLICATION
petobjectPET_HEALTH
abroadStayobjectABROAD_STAY_HEALTH
propertyobjectCOMPULSORY_EARTHQUAKE, HOME_INSURANCE
  • If the item is eligible for more than one type (for example the two types of the DEVICE category), the same pre-fill data is used for each type; a field that is not in a type's schema is ignored for that type.
  • For types sold only without a basket item (PERSONAL_ACCIDENT, FOREIGN_RESIDENT_HEALTH, SUPPLEMENTARY_HEALTH), send the details directly in the quote request: Selling Without a Basket Item.

Validation

The pre-fill data is validated in two steps:

WhenWhat is checkedIf it does not fit
During the requestThat it is a JSON object, the 16 KB limit, root names, the insured list, no basketItemId in the type object, the basic format by field name (date, timestamp, e-mail, phone, country and province code, amount, text length)The request is rejected with 400 VALIDATION_ERROR.
After the request is accepted, against the type's schemaThat the field name exists in the type, identity number check digits, special formats such as IMEI and plate, whether the field applies in that case (for example an airport code on a ticket with transportMode: BUS)The payment does not stop. The item's pre-fill data is not used at all; the fields are asked of the buyer. The reason appears in the insuranceDataIssues field of the Eligibility response.
  • The item's insuranceContext is taken into account during validation: transportMode: FLIGHT in the context makes flightNumber in the pre-fill data valid.
  • If a single field of an item's pre-fill data does not fit, none of that item's pre-fill data is used. Validate the formats on your side before sending: Data Formats.

In errors during the request the field path points to the root; the message names the field under the root. The value you sent is not written into the error response:

{
  "status": "failure",
  "code": "VALIDATION_ERROR",
  "message": "Validation failed — check the submitted fields.",
  "statusCode": 400,
  "requestId": "4b0e6c1a-2f3d-4e5a-9b8c-7d6e5f4a3b2c",
  "details": {
    "errors": [
      { "field": "basketItems", "errors": [] },
      { "field": "basketItems.0.insuranceData.tickets", "errors": ["insuranceData.tickets.departureAt must be an ISO 8601 timestamp with a time zone"] },
      { "field": "basketItems.1.insuranceData.passenger", "errors": ["property passenger should not exist"] }
    ]
  }
}
MessageReason
insuranceData must not be empty; omit the field insteadAn empty object was sent.
insuranceData must be at most 16384 bytes as JSONThe pre-fill data exceeds 16 KB.
property <name> should not existAn unknown root name.
insuranceData.<root> must be a single object (a list type object is sent as one object for the basket item)The type object was sent as an array.
insuranceData.<root>.basketItemId must not be sent; the basket item id is usedbasketItemId was sent in the type object.
insuranceData.insured must be a list of 1-10 objectsinsured is not an array, or it has fewer than 1 or more than 10 elements.
insuranceData.<path> must not be null; omit the field insteadThe field's value is null.
insuranceCategory is required when insuranceTypes, insuranceValueAmount, insuranceContext or insuranceData is sentThe item has no insuranceCategory.

Personal data

  • insuranceData is used only for insurance; it is not passed to the payment or to your payment provider.
  • It is stored encrypted and deleted within at most one hour after the payment is 24 hours old.
  • In request logs the insuranceData field of the payment request is hidden; error responses carry only the field path, not the value. In the eligibility response, personal values are masked.

Details: Insurance › Personal data.

Endpoints that accept it

EndpointinsuranceData
POST /payments/intents basketItems[]Accepted. Stored encrypted; used in the Checkout Form and in quotes created with the payment ID (paymentIntentId).
POST /api/v1/insurance/products context.items[]Accepted. Not stored; the response returns pre-fill details per offer: Eligibility.
POST /api/v1/insurance/quotes context.items[]Accepted. Not stored; merged with the quote body in the same request: Quote › Quote with pre-fill data.
POST /payments/direct/payment, POST /payments/marketplace/intents, /marketplace/2d, /marketplace/3dNot accepted; rejected with 400 like the other insurance fields.
POST /payments/links, PATCH /payments/links/{id}Link items do not have this field (see below).

Several items

If a quote covers several items, the items' pre-fill data is merged as follows:

  • tickets: each item's object, with the item's id, becomes a ticket of the quote.
  • insured: the items' lists are merged into one list in item order. The same identityType and identityNumber is not added a second time; the type's maximum number of insured persons is not exceeded.
  • policyholder: taken from the first item whose pre-fill data gives a policyholder.
  • In types with a single object (device, vehicle and so on) a quote is tied to one item. The basketItemId in the quote request selects whose details are used; if you do not send it, the first item carrying context or pre-fill data is used.

Examples

The values are examples; replace the values in angle brackets with your buyer's details and the reference codes with codes you get from the reference endpoints. Each example shows the case where the item carries everything the quote needs; sending only part of it is also fine.

Bus ticket, TICKET_CANCELLATION. The trip is in the context; the passenger and the ticket amount are in the pre-fill data. tickets is a single object and carries no basketItemId.

"basketItems": [
  {
    "id": "ticket-1",
    "name": "Ankara-Istanbul bus ticket",
    "price": 50000,
    "category1": "ticket",
    "itemType": "VIRTUAL",
    "insuranceCategory": "TICKET",
    "insuranceTypes": ["TICKET_CANCELLATION"],
    "insuranceContext": {
      "transportMode": "BUS",
      "carrier": "Example Carrier",
      "departureAt": "2025-12-23T12:50:00+03:00",
      "originCity": "Ankara",
      "destinationCity": "Istanbul"
    },
    "insuranceData": {
      "insured": [
        {
          "identityType": "PASSPORT",
          "identityNumber": "<passport number>",
          "birthDate": "<YYYY-MM-DD>",
          "email": "<e-mail>",
          "phone": "<E.164 phone>"
        }
      ],
      "tickets": {
        "priceAmount": 50000,
        "pnr": "<booking code>",
        "ticketNumber": "<ticket number>"
      }
    }
  }
]

In a basket with two tickets each item carries its own tickets object and passenger; in the quote the tickets are merged into one tickets list and the passengers into one insured list.

If personal data is sent in insuranceContext, the error shows the item's path; send this information in insuranceData instead:

{ "field": "basketItems.0.insuranceContext.pnr", "errors": ["property pnr should not exist"] }

Payment link items

Payment link items (items[]) use their own format: itemKey, name, unitPriceMinor, minQuantity, maxQuantity and others; see Creating Links. You can add insurance fields to these items too:

FieldIn a link item
insuranceCategoryYes
insuranceTypesYes
insuranceEligibleYes
insuranceValueAmountYes
insuranceContextNo. Trip and booking details are specific to each buyer, while a link is sold to many buyers.
insuranceDataNo. For the same reason: the insured person and quote details are specific to each buyer. The buyer fills them in in the form.
  • The rules are the same as above: if you send insuranceTypes or insuranceValueAmount, insuranceCategory is required.
  • If the buyer selects 3 units of an item, the 3 units are insured separately; the sum insured is the unit price.
  • An undefined field or a wrong type returns 400 VALIDATION_ERROR. A rule violation (for example a type code without a category) returns 422 payment_link.validation.invalid; details.errors shows which field of which item was rejected.

Next