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
| Endpoint | Item | Insurance 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/3d | basketItems[] | Not accepted; the request is rejected with 400 if sent |
POST /payments/links, PATCH /payments/links/{id} (Payment Links) | items[], in its own format | Accepted, except insuranceContext and insuranceData (see below) |
Item fields
| Field | Type | Required | Rule |
|---|---|---|---|
id | string | Yes | Must not be empty. Must be unique within the basket when any item carries insurance fields. |
price | integer | Yes | Minor units, at least 0. The total amount of the line. |
name | string | Yes | Must not be empty. |
category1 | string | Yes | Must not be empty. |
category2 | string | No | — |
itemType | enum | Yes | PHYSICAL or VIRTUAL only. |
Rules:
- An item has no quantity field;
priceis 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'
pricemust equal the request'samount; otherwise the request is rejected with400 VALIDATION_ERROR.amountexcludes 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
basketItemsentry 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
codeandfield, not onmessage. 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.
| Field | Type | Rule | Meaning |
|---|---|---|---|
insuranceCategory | enum | A value from the item category dictionary | The item's category. The eligible insurance types are resolved from this field. |
insuranceTypes | string[] | Each element ^[A-Z][A-Z0-9_]{2,63}$; unique; at most 10 elements | Insurance type codes; narrows the eligible types. If omitted or empty, all types of the item category. |
insuranceEligible | boolean | true or false | If false, no insurance is offered for this item; takes precedence over the other insurance fields. |
insuranceValueAmount | integer | Minor units, at least 1 | The insured value of one unit. If omitted, the unit price is used. |
insuranceContext | object | Allowed keys only; an unknown key is rejected | Non-personal details (trip, device model, travel dates and so on). Pre-fills the fields of the insurance type: Pre-filling. |
insuranceData | object | Insurance pre-fill data; at most 16 KB | The 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,insuranceContextorinsuranceData,insuranceCategoryis required.insuranceEligible: falsecan 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
idstarts withinso_) carries no insurance fields; if it does, the request is rejected with400 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
| Value | Item |
|---|---|
TICKET | Bus, flight or train ticket |
HOTEL | Accommodation booking |
EVENT | Event ticket |
DEVICE | Mobile phone |
ELECTRONICS | Electronics, home appliances |
VEHICLE | Vehicle |
TRAVEL | Travel, tour package |
HOME | Home |
HEALTH | Health service |
PET | Pet |
MICROMOBILITY | Rented scooter and similar micromobility |
MUSICAL_INSTRUMENT | Musical instrument |
SHIPMENT | Freight, parcel shipment |
EDUCATION | Education, 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:
| Key | Format |
|---|---|
transportMode | BUS, FLIGHT or TRAIN |
carrier | Text, 1-100 characters |
departureAt | Timestamp, time zone required (2025-12-23T12:50:00+03:00) |
originCity | Text, 1-100 characters |
destinationCity | Text, 1-100 characters |
originAirportCode | IATA, three upper-case letters |
destinationAirportCode | IATA, three upper-case letters |
flightNumber | Text, 1-100 characters |
isPromotional | true or false |
HOTEL:
| Key | Format |
|---|---|
hotelName | Text, 1-200 characters |
checkInDate | Date, YYYY-MM-DD |
checkOutDate | Date, YYYY-MM-DD |
guestCount | Integer, at least 1 |
cityCode | Province plate code, 01-81 |
DEVICE:
| Key | Format |
|---|---|
brandCode | Lunixi reference code |
modelCode | Lunixi reference code |
coverageStartDate | Date, YYYY-MM-DD |
ELECTRONICS:
| Key | Format |
|---|---|
categoryCode | Lunixi reference code |
brand | Text, 1-100 characters |
model | Text, 1-100 characters |
warrantyMonths | 12 or 24 (number) |
VEHICLE:
| Key | Format |
|---|---|
brandCode | Lunixi reference code |
modelCode | Lunixi reference code |
modelYear | Integer |
vehicleTypeCode | Lunixi reference code |
vehicleUsageCode | Lunixi reference code (usage) |
seatCount | Integer, number of seats including the driver |
TRAVEL:
| Key | Format |
|---|---|
scope | DOMESTIC or INTERNATIONAL |
startDate | Date, YYYY-MM-DD |
endDate | Date, YYYY-MM-DD |
destinationCountryCodes | Array; each entry ISO 3166-1 alpha-2, at most 20 entries |
EVENT and MICROMOBILITY:
| Key | Format |
|---|---|
coverageStartDate | Date, YYYY-MM-DD |
MUSICAL_INSTRUMENT:
| Key | Format |
|---|---|
categoryCode | Lunixi reference code |
brand | Text, 1-100 characters |
model | Text, 1-100 characters |
HEALTH:
| Key | Format |
|---|---|
procedureCoverCode | Lunixi reference code |
procedureDate | Date, YYYY-MM-DD |
facilityName | Text, 1-200 characters |
PET:
| Key | Format |
|---|---|
petSpecies | CAT or DOG |
petBreedCode | Lunixi reference code |
EDUCATION:
| Key | Format |
|---|---|
startDate | Date, YYYY-MM-DD |
endDate | Date, YYYY-MM-DD |
HOME:
| Key | Format |
|---|---|
areaSqm | Integer, gross area (square metres), 1-100000 |
constructionYear | Integer, the building's construction year |
dwellingTypeCode | Lunixi reference code (dwelling type) |
SHIPMENT:
| Key | Format |
|---|---|
originCityCode | Origin province, plate code 01-81 |
destinationCityCode | Destination province, plate code 01-81 |
departureDate | Date, 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.
insuranceContextcarries 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 asidentityNumber,firstName,birthDate) are sent here, the request is rejected. To send these details, use theinsuranceDatafield.- Amount fields are not sent here; the insured value is in
insuranceValueAmount. You can give the type's amount field (for example the ticket amountpriceAmount) ininsuranceData. - 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 type | Type object | Fields filled from the context |
|---|---|---|
TICKET_CANCELLATION | tickets | transportMode, carrier, departureAt, originCity, destinationCity, originAirportCode, destinationAirportCode, flightNumber, isPromotional |
HOTEL_CANCELLATION | stay | hotelName, checkInDate, checkOutDate, guestCount, cityCode |
DEVICE_INSURANCE, DEVICE_PROTECTION | device | brandCode, modelCode, coverageStartDate |
EXTENDED_WARRANTY | product | categoryCode, brand, model, warrantyMonths |
ROADSIDE_ASSISTANCE | vehicle | brandCode, modelCode, modelYear, vehicleTypeCode |
VEHICLE_TAG_PROTECTION | vehicle | coverageStartDate |
MOTOR_GREEN_CARD | vehicle | seatCount, vehicleTypeCode, startDate, endDate |
MOTOR_THIRD_PARTY_LIABILITY | vehicle | modelCode, modelYear, vehicleTypeCode |
MOTOR_COMPREHENSIVE | vehicle | modelCode, modelYear, vehicleTypeCode, coverageStartDate |
MOTOR_EXCESS_LIABILITY | vehicle | modelCode, modelYear, vehicleTypeCode, vehicleUsageCode, coverageStartDate |
TRAVEL_HEALTH | trip | scope, startDate, endDate, destinationCountryCodes, originCityCode |
INBOUND_TRAVEL_HEALTH | trip | startDate, endDate |
ABROAD_STAY_HEALTH | abroadStay | startDate, endDate |
MICROMOBILITY_ACCIDENT | rental | coverageStartDate |
MUSICAL_INSTRUMENT_PROTECTION | instrument | brand, model, categoryCode |
EVENT_ACCIDENT | event | coverageStartDate |
MEDICAL_COMPLICATION | procedure | procedureCoverCode, procedureDate, facilityName, startDate, endDate |
PET_HEALTH | pet | coverageStartDate, petSpecies, petBreedCode |
COMPULSORY_EARTHQUAKE | property | coverageStartDate, areaSqm, constructionYear |
HOME_INSURANCE | property | areaSqm, constructionYear, dwellingTypeCode |
- A key that is not in the table is not used for that type; for example
warrantyMonthsforDEVICE_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: BUSis ignored. - If the same field is also in
insuranceData, the value ininsuranceDataapplies. 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 send | Result |
|---|---|
| Nothing | As today: the buyer fills in the details in the form, or you send them in the quote request. |
| Part of the details | The 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 needs | The 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,insuredand 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": "…" }. basketItemIdis not sent in the type object; the item'sidis used. If it is sent, the request is rejected.insuredis 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.
nullvalues and an empty object ({}) are not accepted. - At most 16 KB as JSON (UTF-8).
| Root | Format | Types that use it |
|---|---|---|
policyholder | object | The policyholder; person fields |
insured | array, 1-10 objects | The insured persons; person fields |
tickets | object | TICKET_CANCELLATION |
stay | object | HOTEL_CANCELLATION |
device | object | DEVICE_INSURANCE, DEVICE_PROTECTION |
product | object | EXTENDED_WARRANTY |
vehicle | object | ROADSIDE_ASSISTANCE, VEHICLE_TAG_PROTECTION, MOTOR_GREEN_CARD, MOTOR_THIRD_PARTY_LIABILITY, MOTOR_COMPREHENSIVE, MOTOR_EXCESS_LIABILITY |
trip | object | TRAVEL_HEALTH, INBOUND_TRAVEL_HEALTH |
rental | object | MICROMOBILITY_ACCIDENT |
instrument | object | MUSICAL_INSTRUMENT_PROTECTION |
event | object | EVENT_ACCIDENT |
procedure | object | MEDICAL_COMPLICATION |
pet | object | PET_HEALTH |
abroadStay | object | ABROAD_STAY_HEALTH |
property | object | COMPULSORY_EARTHQUAKE, HOME_INSURANCE |
- If the item is eligible for more than one type (for example the two types of the
DEVICEcategory), 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:
| When | What is checked | If it does not fit |
|---|---|---|
| During the request | That 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 schema | That 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
insuranceContextis taken into account during validation:transportMode: FLIGHTin the context makesflightNumberin 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"] }
]
}
}| Message | Reason |
|---|---|
insuranceData must not be empty; omit the field instead | An empty object was sent. |
insuranceData must be at most 16384 bytes as JSON | The pre-fill data exceeds 16 KB. |
property <name> should not exist | An 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 used | basketItemId was sent in the type object. |
insuranceData.insured must be a list of 1-10 objects | insured is not an array, or it has fewer than 1 or more than 10 elements. |
insuranceData.<path> must not be null; omit the field instead | The field's value is null. |
insuranceCategory is required when insuranceTypes, insuranceValueAmount, insuranceContext or insuranceData is sent | The item has no insuranceCategory. |
Personal data
insuranceDatais 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
insuranceDatafield 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
| Endpoint | insuranceData |
|---|---|
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/3d | Not 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'sid, becomes a ticket of the quote.insured: the items' lists are merged into one list in item order. The sameidentityTypeandidentityNumberis 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,vehicleand so on) a quote is tied to one item. ThebasketItemIdin 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:
| Field | In a link item |
|---|---|
insuranceCategory | Yes |
insuranceTypes | Yes |
insuranceEligible | Yes |
insuranceValueAmount | Yes |
insuranceContext | No. Trip and booking details are specific to each buyer, while a link is sold to many buyers. |
insuranceData | No. 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
insuranceTypesorinsuranceValueAmount,insuranceCategoryis 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) returns422 payment_link.validation.invalid;details.errorsshows which field of which item was rejected.
Next
- Checkout Form: the payment request you send the items in
- Insurance: how insurance works
- Data Formats: timestamp, date and code formats