Bus ticketsbus_bookings
/api/v1/imports/bus_bookingsCreate-once: re-sending the same external_id skips rather than duplicating.
Endpoints
- validatePOST /api/v1/imports/bus_bookings/validate
- importPOST /api/v1/imports/bus_bookings
- feedGET /api/v1/imports/bus_bookings/records
- read_backGET /api/v1/imports/bus_bookings/records/{external_id}
Sample request billed by its bill-to (no customer key)
curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/imports/bus_bookings/validate' \
-H 'Authorization: Bearer sek_YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
-d '{
"source_system": "crm",
"rows": [
{
"external_id": "BUS-BOOKING-1001-B",
"currency": "EUR",
"supplier_external_id": "SUPPLIER-1001",
"billing_basis": "agent",
"fare_collected_by": "agency",
"status": "draft",
"reference": "Reference",
"supplier_confirmation_no": "Supplier confirmation no",
"notes": "Imported from the source system.",
"option_date": "2026-03-20T18:00",
"transaction_date": "2026-03-14",
"invoice_date": "2026-03-18",
"legs": [
{
"origin_name": "İstanbul",
"origin_code": "IST",
"destination_name": "Ankara",
"destination_code": "ANK",
"departs_at": "2026-03-14T09:30:00+03:00",
"arrives_at": "2026-03-14T12:45:00+03:00",
"service_number": "S-101",
"operator_name": "Express Lines",
"fare_name": "Standard"
}
],
"travelers": [
{
"first_name": "Ada",
"last_name": "Lovelace",
"nationality": "TR",
"national_id": "10000000146",
"passport_number": "U01234567",
"passport_country": "TR",
"passport_expiry": "2031-06-30",
"date_of_birth": "1988-04-21",
"gender": "MRS",
"frequent_flyer_number": "TK123456789",
"passenger_type": "adult",
"phone": "+905551112233",
"fare": "850.00",
"seats": [
{
"leg": 0,
"seat_number": "12",
"coach_number": "3",
"ticket_number": "BUS-2026-000101",
"fare_amount": "850.00"
}
]
}
],
"cost_lines": [
{
"kind": "ticket_fare",
"amount": "1250.00",
"label": "Imported line",
"currency": "EUR",
"vat_rate": "20",
"tax_inclusive": true,
"quantity": 1
}
],
"sell_lines": [
{
"kind": "package_price",
"amount": "1250.00",
"label": "Imported line",
"currency": "EUR",
"vat_rate": "20",
"tax_inclusive": true,
"quantity": 1
}
],
"bill_to_legal_name": "Lovelace Seyahat A.Ş.",
"bill_to_tax_number": "1234567890",
"bill_to_tax_office": "Beşiktaş",
"bill_to_address_line": "Nispetiye Cad. No:12",
"bill_to_city": "İstanbul",
"bill_to_country": "TR"
}
]
}'Fields(36)
external_idstringcurrencycurrencyomit for the company's own
customer_external_idreferencethe cari; omit it for a walk-in sale billed to the first passenger
supplier_external_idreferencethe carrier
billing_basisenumagent ⇒ the fare is the carrier's money and the agency bills its own fee; gross ⇒ the agency resells the fare. Omit for the company's default (agent in Türkiye)
One of:
agentgrossfare_collected_byenumwho took the fare from the passenger; a gross sale is always the agency's
One of:
agencycarrierstatusenumdefault issued;
cancelledon an amendment cancels the bookingOne of:
draftoptionissuedcancelledreferencestringthe carrier's booking reference (PNR)
supplier_confirmation_nostringnotesstringoption_datedatetransaction_datedateişlem tarihi — the FX anchor for every line
invoice_datedatecustom_fieldsobjectthe account's own fields on the booking, by key
legsobject[]the journey in order; a seat names its leg by position
Each item
origin_namestringorigin_codestringdestination_namestringdestination_codestringdeparts_atdatetimearrives_atdatetimeservice_numberstringthe trip or train number
operator_namestringfare_namestring
travelersobject[]PIIpassengers; auto-saved to the cari's traveller directory. The first is the walk-in payer
Each item
first_namestringPIIlast_namestringPIInationalitystringnational_idstringPIIpassport_numberstringPIIpassport_countrystringpassport_expirydatedate_of_birthdatePIIgenderstringValues the app uses:
MRMRSMISSMSTRfrequent_flyer_numberstringpassenger_typeenumOne of:
adultchildinfantphonephonePIIfaremoneythe carrier's fare for this passenger, written as its ticket_fare sell and cost line
custom_fieldsobjectthe account's own fields on the passenger, by key
seatsobject[]one per leg the passenger rides
Each item
legintegerthe leg's position in
legs, from 0seat_numberstringcoach_numberstringticket_numberstringthe e-ticket number
fare_amountmoneythis leg's share of the fare
cost_linesobject[]cost-side pricing lines for the "bus" pricing context
Each item
kindstringrequiredValues the app uses:
ticket_farecarrier_commissionapplied_fareservice_feesupplier_refundinsurance_premiumamountmoneyrequiredlabelstringcurrencycurrencyvat_ratedecimalpercent (20 ⇒ 20% KDV); omit to take the kind's default
tax_inclusivebooleantrue ⇒ the amount includes VAT. A hotel stay or a charter takes one VAT mode: the lines' flags decide it, lines stating both are refused, and every line follows the booking's mode afterwards
quantityinteger
sell_linesobject[]sell-side pricing lines for the "bus" pricing context
Each item
kindstringrequiredValues the app uses:
package_pricemarkupservice_feesupplier_commissioncancellation_feeinsurance_premiumdiscountrefund_allowanceticket_farecarrier_commissionapplied_faresupplier_refundamountmoneyrequiredlabelstringcurrencycurrencyvat_ratedecimalpercent (20 ⇒ 20% KDV); omit to take the kind's default
tax_inclusivebooleantrue ⇒ the amount includes VAT. A hotel stay or a charter takes one VAT mode: the lines' flags decide it, lines stating both are refused, and every line follows the booking's mode afterwards
quantityinteger
service_feemoneythe agency's fee for the whole booking, in its currency; split across the guests like the booking form's. Sent, the account's service-fee rules write nothing
service_fee_percentdecimalthe fee as a percentage (0-100) of the booking's sale before fee and discount, instead of
service_fee; split like itservice_fee_vat_ratedecimalthe fee's VAT, percent (0 ⇒ exempt); omit it and the law decides, else the seller's standard rate
service_fee_waivedbooleantrue: the account's service-fee rules write no fee on this booking
bill_to_typeenumomit to infer: a legal name or a 10-digit tax number ⇒ corporate
One of:
corporateindividualbill_to_legal_namestringbill_to_tax_numbertax_numberPIIVKN for a corporate payer, TCKN for an individual; matched digits-only
bill_to_tax_officestringbill_to_passport_numberstringPIIindividual only; used when there is no TCKN
bill_to_first_namestringPIIbill_to_last_namestringPIIbill_to_emailemailPIIstored on a NEW cari; never used to match
bill_to_phonephonePIIstored on a NEW cari; never used to match
bill_to_address_linestringPIIbill_to_districtstringPIIbill_to_citystringPIIbill_to_zipstringPIIbill_to_countrystringISO-2 code, e.g. TR
Your account's own fields (custom_fields)
Each agency adds its own fields in Settings → Custom fields: a project code, a trip reason, an employee number. They differ from account to account, so this page cannot list them. Your integration asks the API for them.
GET /api/v1/imports/entities/{key} answers, under custom_fields, the fields your key's account has on this entity: record for the record itself, traveler for each traveller. Each one names its key (what the row sends), its type, whether it is required, which customers it applies_to and, for a choice, its options. Read it when you build the mapping and again when the agency says it changed a field: it is the account's live list.
Request · the account's fields
curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/imports/entities/bus_bookings' \
-H 'Authorization: Bearer sek_YOUR_API_KEY'{
"key": "bus_bookings",
"custom_fields": {
"record": [
{
"key": "project_code",
"label": "Project code",
"type": "text",
"required": "always",
"applies_to": "all"
},
{
"key": "trip_reason",
"label": "Trip reason",
"type": "select",
"required": "listed_customers",
"applies_to": "listed_customers",
"options": [
{
"value": "meeting",
"label": "Meeting"
},
{
"value": "fair",
"label": "Trade fair"
}
],
"customers": [
{
"id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
"external_id": "CUST-17",
"required": true
}
]
}
],
"traveler": [
{
"key": "employee_no",
"label": "Employee no",
"type": "text",
"required": "never",
"applies_to": "all",
"pii": true
}
]
}
}What each type takes
| type | value in the row |
|---|---|
text | "PRJ-2026-001" |
long_text | "Fair delegation, second group" |
number | 12.5 |
date | "2026-03-14" |
boolean | true |
select | "meeting" |
multi_select | ["meeting", "fair"] |
number takes a number or a numeric string; date an ISO date; select one
option value (or its label); multi_select a list of them.
required and applies_to
requiredneveralwayslisted_customerslisted_customers: required only for the customers markedrequiredincustomers.applies_toalllisted_customerslisted_customers: the field is accepted only on those customers' records.
Send the values as one JSON object by key. An unknown key, a value of the wrong type or a missing required field fails the row and names the key. On an amendment (on_conflict: update) the object merges by key: a key you leave out is kept, a key sent as null is removed. A field the agency prints on the customer's documents cannot change once the record is invoiced.
Traveller fields go on each traveller, under that item's own custom_fields.
They come back the same way: under detail.custom_fields in the read-back and the webhook body (and on each passenger of a ticket). A field marked personal comes back only to a key whose role can read personal data, and never in a webhook.
Reading it back
Once the agency works the record (confirms it, numbers the tickets, invoices it) this is what GET /api/v1/imports/{entity}/records/{external_id} answers, and what every webhook delivers. detail carries what only the agency can add after your push.
{
"entity": "bus_bookings",
"external_id": "BUS-BOOKING-1001",
"source_system": "crm",
"type": "BusBooking",
"id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
"created_at": "2026-03-14T09:30:00Z",
"updated_at": "2026-03-15T11:05:00Z",
"status": "issued",
"locked": true,
"lock_reason": "invoiced",
"currency": "EUR",
"totals": {
"cost": "1100.0",
"sell": "1250.0"
},
"invoices": [
{
"id": "4b8e1f60-7c2d-4e9a-b3f5-0a6d2c9e8b71",
"number": "LYD2026000000117",
"status": "issued",
"currency": "EUR",
"total": "1250.0",
"balance": "1250.0"
}
],
"detail": {
"voucher_number": "VCH-2026-000412",
"supplier_confirmation_no": "CNF-77821",
"supplier": "Lovelace Travel Partners",
"custom_fields": {
"project_code": "PRJ-2026-001",
"trip_reason": "meeting"
},
"route": "İstanbul → Ankara",
"departs_at": "2026-03-14T06:30:00Z",
"billing_basis": "agent",
"option_date": "2026-03-10"
}
}Request · dry run, every field
curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/imports/bus_bookings/validate' \
-H 'Authorization: Bearer sek_YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
-d '{
"source_system": "crm",
"rows": [
{
"external_id": "BUS-BOOKING-1001",
"currency": "EUR",
"customer_external_id": "CUSTOMER-1001",
"supplier_external_id": "SUPPLIER-1001",
"billing_basis": "agent",
"fare_collected_by": "agency",
"status": "draft",
"reference": "Reference",
"supplier_confirmation_no": "Supplier confirmation no",
"notes": "Imported from the source system.",
"option_date": "2026-03-20T18:00",
"transaction_date": "2026-03-14",
"invoice_date": "2026-03-18",
"custom_fields": {
"project_code": "PRJ-2026-001",
"trip_reason": "meeting"
},
"legs": [
{
"origin_name": "İstanbul",
"origin_code": "IST",
"destination_name": "Ankara",
"destination_code": "ANK",
"departs_at": "2026-03-14T09:30:00+03:00",
"arrives_at": "2026-03-14T12:45:00+03:00",
"service_number": "S-101",
"operator_name": "Express Lines",
"fare_name": "Standard"
}
],
"travelers": [
{
"first_name": "Ada",
"last_name": "Lovelace",
"nationality": "TR",
"national_id": "10000000146",
"passport_number": "U01234567",
"passport_country": "TR",
"passport_expiry": "2031-06-30",
"date_of_birth": "1988-04-21",
"gender": "MRS",
"frequent_flyer_number": "TK123456789",
"passenger_type": "adult",
"phone": "+905551112233",
"fare": "850.00",
"custom_fields": {
"employee_no": "E-10442"
},
"seats": [
{
"leg": 0,
"seat_number": "12",
"coach_number": "3",
"ticket_number": "BUS-2026-000101",
"fare_amount": "850.00"
}
]
}
],
"cost_lines": [
{
"kind": "ticket_fare",
"amount": "1250.00",
"label": "Imported line",
"currency": "EUR",
"vat_rate": "20",
"tax_inclusive": true,
"quantity": 1
}
],
"sell_lines": [
{
"kind": "package_price",
"amount": "1250.00",
"label": "Imported line",
"currency": "EUR",
"vat_rate": "20",
"tax_inclusive": true,
"quantity": 1
}
],
"service_fee": "1250.00",
"service_fee_vat_rate": "20",
"service_fee_waived": true
}
]
}'{
"import": {
"id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
"entity": "bus_bookings",
"source_system": "crm",
"status": "ready",
"company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
"stats": {
"total": 1,
"will_create": 1,
"will_update": 0,
"created": 0,
"updated": 0,
"skipped": 0,
"invalid": 0,
"failed": 0,
"not_selected": 0
},
"error": null,
"created_at": "2026-03-14T09:30:00Z",
"links": {
"self": "/api/v1/imports/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
"rows": "/api/v1/imports/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18/rows"
},
"mode": "validate",
"async": false,
"on_conflict": "skip",
"pricing": "apply_rules"
},
"ignored_fields": [],
"results": [
{
"row": 1,
"external_id": "BUS-BOOKING-1001",
"status": "will_create",
"payer": {
"outcome": "external_id",
"customer_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18"
}
}
]
}