Ketendo

Transportation

Trains, coaches, journeys, passengers and crew.

UAT https://uat-ketendo-api.azurewebsites.net  ·  Production https://ketendo-api.azurewebsites.net  ·  Last updated 2026-09-21
Required must be sent, and valid, or the call fails Conditional required only in some situations (explained inline) Optional can be left out Auto-set the server fills this in - don't send it
What this module covers

Transportation models the physical fleet and the journeys run on it: assets (trains), parts (coaches), compartments (suites and seats), routes and their stoppages, journeys, and the passengers, bookings and crew attached to them - plus the lookups those records depend on.

Before your first call, read Conventions for the ExternalId upsert rule, the required CompanyId and the X-Source-System header, and Authentication for tokens. They apply to everything here.

Some records point at other records (a coach points at a part type, a journey points at a route). Upload in this order and everything will resolve cleanly:

  1. Lookups - PartCategory → PartType, CompartmentType, WindowType, ToiletType, WheelType, Facility, TripStatus, PromotionalStatus, PackageDepartureStatus, JourneyCode, Stoppage, ItinerarySupplier
  2. Members (internal users/crew) - only needed if you'll reference them from TripMember
  3. Passengers (people)
  4. Assets (trains)
  5. Parts (coaches) - needs PartType
  6. PartCompartments (suites/seats in a coach) - needs Part, CompartmentType
  7. LinkedAssetParts (attach coaches to a train) - needs Asset, Part
  8. RouteInfo - needs JourneyCode and an existing Client; include RouteStoppages if you have them, needs Stoppage saved first
  9. SiteTripInfo (create the journey) - needs a Site that is already linked to your company in Ketendo (set up via the Ketendo portal first - this API can't create that link); include TripStoppages if the route has stoppages, needs RouteInfo's RouteStoppages saved first
  10. TripParts (attach coaches to the journey)
  11. TripCompartments (publish a seat/suite to the journey)
  12. TripPassenger (assign a booking to a seat)
  13. TripMember (assign crew to the journey)

Master data / lookups

Upload these first - the core entities below reference them by id. All 13 endpoints share the same shape: an ExternalId plus one nested lookup object with Name, Code, Description, Order.

We checked the save logic for all of these: Order is not honoured the very first time you create a record (Ketendo auto-assigns it) - if you send the same ExternalId again to update it, your Order value is then applied.
POST/v1/integration/transportation/SaveGenderGender option for passengers
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key for this gender, e.g. GENDER-F
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
GenderobjectRequiredThe gender details
NamestringRequiredDisplay name, e.g. Female. Max 256 characters
CodestringRequiredShort code, e.g. F. Must be unique within your company - see the callout below. Max 256 characters
DescriptionstringOptional
SelectableboolOptionalWhether it appears in pick-lists in the Ketendo portal. Send true for anything staff should be able to choose; default false
OrderintOptionalDisplay order. Left out on a create, we append it to the end of the list for you
DeletedboolOptionalSend true to retire an option without removing it from passengers already using it
Id, ExternalIdentifier-Auto-setDon't send
Code is unique per company. If you send a Code that another gender on the same company already uses, the call fails on a unique-constraint violation. Because of that, treat Code as fixed once created - to rename an option change Name, not Code. Name and Code are both non-nullable in the database, so neither can be omitted or sent as null.
The internalId in the response is the value you then send as Passenger.GenderId on SavePassenger. Genders are configured per company, so save them once per company and keep the mapping.
POST/v1/integration/transportation/SavePartCategoryCoach category (e.g. kitchen, bar)
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key for this category, e.g. CATEGORY-LOCO
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartCategoryobjectRequiredThe category details
NamestringOptionalDisplay name
CodestringOptionalShort code
DescriptionstringOptional
OrderintOptionalDisplay order, default 0
Id-Auto-setDon't send
POST/v1/integration/transportation/SavePartTypeCoach type (e.g. loco, sleeper, diner)
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. TYPE-SLEEPER
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartTypeobjectRequired
Name, Code, Description, Order-OptionalSame as PartCategory above
PartCategoryIdintRequiredMust reference an existing PartCategory - save that first
Id-Auto-setDon't send
PartCategoryId is the internal numeric id returned when you saved the PartCategory - not its ExternalId. Keep a lookup table of ExternalId → returned id/InternalId as you go, or re-resolve it from the response of the earlier call.
POST/v1/integration/transportation/SaveWindowTypeWindow style
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. WINDOW-LARGE
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
WindowTypeobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SaveToiletTypeToilet type
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. TOILET-VACUUM
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
ToiletTypeobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SaveWheelTypeWheel / bogie type
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. WHEEL-BOGIE-4
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
WheelTypeobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SaveCompartmentTypeSuite / seat type
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. COMP-SUITE-1B
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
CompartmentTypeobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
You'll need the returned internal id for this when you call SavePartCompartments.
POST/v1/integration/transportation/SaveFacilityFacility / amenity (e.g. WiFi)
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. FAC-WIFI
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
FacilityobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SaveTripStatusJourney status (e.g. On Time, Delayed)
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. STATUS-ON-TIME
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
TripStatusobjectRequired
Name, Code, Description, Order-Optional
Colour, TextColourstringOptionalHex colour for UI display, e.g. #00FF00
IsDefaultboolOptionalMarks this as the default status, default false
Id-Auto-setDon't send
POST/v1/integration/transportation/SavePromotionalStatusPromo / special status
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. PROMO-FULL
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PromotionalStatusobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SavePackageDepartureStatusDeparture readiness status
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. DEP-READY
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PackageDepartureStatusobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
POST/v1/integration/transportation/SaveJourneyCodeJourney / timetable code
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. JC-0600-JNB-CPT
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
JourneyCodeobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
Needed for SaveRouteInfo below.
POST/v1/integration/transportation/SaveStoppageIntermediate stop on a route
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. STOP-BEAUFORT-WEST
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
StoppageobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
Needed for SaveRouteInfo's optional RouteStoppages below - save every Stoppage you plan to reference first.
POST/v1/integration/transportation/SaveItinerarySupplierSupplier providing an itinerary activity
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. SUPPLIER-KAROO-TOURS
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
ItinerarySupplierobjectRequiredName/Code/Description/Order all Optional; Id Auto-set
Referenced only by name/description today - there's no supplier id field on the itinerary items below. Save it here for your own record-keeping and lookup by ExternalId.

Core entities

POST/v1/integration/transportation/SavePassengerCreate/update a passenger
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key for this person
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PassengerobjectRequired
IdentificationNumberstringOptionalID/passport number
PassportNumberstringOptional
IsRepeatGuest, IsSmoker, Visa, AllowSharingOfDataboolOptionalDefault false if omitted
OccupationstringOptional
DateOfBirthdate?OptionalDate of birth. Send ISO-8601 - "1985-03-14" or "1985-03-14T00:00:00Z" both work. Omitting it leaves any existing value in place
GenderIdint?OptionalThe internal id of a Gender configured for your company - not a code or a name. See the callout below for how to fetch the list, or create them yourself with SaveGender
CompanyCountryIdguid?OptionalThe passenger's country, as configured for your company
ContactobjectOptionalName/email/phone for this passenger - see below
↳ ↳ Name, Email, WebsitestringOptional
↳ ↳ CompanyTitleIdguid?Optionale.g. Mr/Mrs/Dr, as configured for your company
↳ ↳ ContactNumbersarrayOptionalPhone numbers - see below
↳ ↳ ↳ NumberstringOptional
↳ ↳ ↳ ContactNumberTypeIdintOptionale.g. mobile/landline, as configured for your company
↳ ↳ ↳ IsDefaultboolOptionalDefault false
PassengerActionsarrayOptionalNotes/log entries against this passenger - rarely needed for integration. On updates, only brand-new entries are added; changes to an entry you already sent before are not applied
↳ ↳ ActionTypeIdintOptionalAs configured for your company
↳ ↳ Name, Notes, AdditionalNotesstringOptional
Id, ExternalIdentifier-Auto-setDon't send
Getting your GenderId values. Genders are configured per company, so the ids on your company are not the same as anyone else's. Fetch them once with POST /v1/LookupLists/GenderListItems sending { "Identifier": <your CompanyId>, "PageNumber": 1, "PageSize": 100 } - the response gives you each gender's id and name. Cache the mapping in your source system rather than looking it up per passenger. Same idea as CompanyTitleId and ContactNumberTypeId above.
Send Contact the first time you save this passenger. We checked: if the passenger already exists and doesn't have a Contact yet, sending one on a later update call is silently ignored rather than adding it - there's no way to add a Contact after the fact through this endpoint today.
POST/v1/integration/transportation/SaveAssetCreate/update a train / asset
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key, e.g. TRAIN-001-2026
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
AssetobjectRequired
Code, Name, DescriptionstringOptional
UsedForTravelPurposesboolOptionalDefault false
Id-Auto-setDon't send
POST/v1/integration/transportation/SavePartCreate/update a coach / part
FieldTypeRequired?Notes
ExternalIdstringRequirede.g. COACH-A01
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartobjectRequired
Code, Name, DescriptionstringOptional
PartTypeIdintRequiredMust reference an existing PartType (internal id, not ExternalId)
Id-Auto-setDon't send
WindowTypeId, ToiletTypeId and WheelTypeId exist on the Part object but this endpoint does not save them - we verified the underlying save method never reads them. Use SavePartFacilities to set those three fields on a coach.
POST/v1/integration/transportation/SavePartCompartmentsAdd/update suites/seats inside a coach (batch of many per call)
FieldTypeRequired?Notes
PartExternalIdstringConditionalThe coach these belong to - required unless you pass PartId instead
PartIdguidConditionalInternal id alternative to PartExternalId
CompanyIdintRequiredWhich company these records belong to - see the CompanyId note
CompartmentsarrayRequiredAt least one item
ExternalIdstringRequiredYour unique key for this seat/suite
Name, DescriptionstringOptional
CompartmentTypeIdintRequiredMust reference an existing CompartmentType
DeletedboolOptionalSet true to remove a previously-synced compartment; default false
This call replaces the full set of compartments for the coach, not just the ones you send. Any compartment saved previously that is left out of the Compartments list on a later call is automatically removed (soft-deleted) - always send the complete current list for this coach, not just new/changed items.
POST/v1/integration/transportation/SaveLinkedAssetPartsAttach coaches to a train
FieldTypeRequired?Notes
AssetExternalIdstringConditionalRequired unless AssetId is given
AssetIdguidConditionalInternal id alternative to AssetExternalId
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartExternalIdsarray of stringConditionalRequired (non-empty) unless PartIds is given
PartIdsarray of guidConditionalInternal ids alternative to PartExternalIds
All parts referenced must already exist (save them with SavePart first) - unknown part external ids fail the whole call.
This call replaces the full set of coaches linked to the train, not just the ones you send. Any coach linked previously that is left out of the list on a later call is automatically unlinked - always send the complete current list of coaches for this train. Only the link itself is removed; the coach (Part) record is untouched.
POST/v1/integration/transportation/SaveRouteInfoCreate/update a route
FieldTypeRequired?Notes
ExternalIdstringRequired
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
ClientExternalIdstringConditionalRequired unless RouteInfo.ClientId is already set to a valid guid
RouteInfoobjectRequired
RouteName, PackageCode, Colour, TextColourstringOptional
JourneyCodeIdintRequiredMust reference an existing JourneyCode
Durationint?Optional
ClientIdguidConditionalAlternative to ClientExternalId at the wrapper level
ExternalIdentifier-Auto-setDon't send
RouteStoppagesarrayOptionalIntermediate stops for this route, in order
StoppageIdintRequiredInternal id of an existing Stoppage - save that first and use the internalId from its response
Order, DurationintOptional
ItineraryItemsarrayOptionalActivities at this stop
↳ ↳ Name, Code, DescriptionstringOptional
↳ ↳ OrderintOptional
↳ ↳ DeletedboolOptionalDefault false
↳ ↳ ExternalIdentifierstringOptionalYour own key for this specific itinerary item, so you can update it later without knowing its internal id
↳ ↳ Id, RouteStoppageId-Auto-setDon't send
There can only be one route per Client under your company - we verified the saved record's internal id is always derived from the Client, not from your ExternalId. Calling this again for the same Client (even with a different ExternalId) updates that same route rather than creating a second one. Use one RouteInfo call per Client, and treat JourneyCodeId/RouteName as "the route currently assigned to this client," not as an independent list of routes.
The referenced Client must already exist in Ketendo (clients are managed via the separate /v1/integration/ client endpoints, not covered in this Transportation guide).
If you send RouteStoppages at all, it replaces the full set of stoppages on the route - any stoppage previously saved whose StoppageId is left out of the list is removed, itinerary items included. Leave RouteStoppages out entirely to update only the route's own fields and leave its stoppages untouched. Give each itinerary item you want to update again later an ExternalIdentifier - items sent without one are always treated as new.
POST/v1/integration/transportation/SaveSiteTripInfoCreate a journey (trip) at a site
The Site must already be linked to your company in Ketendo (done via the Ketendo portal UI) before this call will work. If it isn't, you'll get an error like "CompanySite record not found" - that's a setup step on our side, not something you can fix from your payload.
FieldTypeRequired?Notes
SiteExternalIdstringConditionalRequired unless Request.Site.Id is already a valid guid
TripExternalIdstringConditionalRequired unless Request.TripInfo.Id is already a valid guid
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
RequestobjectRequired
SiteobjectRequiredThe site the journey departs from
↳ ↳ Name, CodestringOptionalMust be unique per Client - reusing a Code already used by another Site under the same Client fails
↳ ↳ ClientIdguidRequiredExisting client this site belongs to - the Client must already have a company relationship set up in Ketendo, or creating a new Site fails outright
↳ ↳ VATNumberstringOptional
↳ ↳ ShippingAddress, BillingAddressstringOptional
↳ ↳ UseAsBillingAddressboolOptionalDefault false
↳ ↳ CountryId, ProvinceIdint?Optional
↳ ↳ SiteTypeIdint?OptionalSend it to categorize the site for your company
↳ ↳ DateActioneddatetime?Optional
↳ ↳ ExternalIdentifier, CompanyId-Auto-setDon't send - both are set from the wrapper-level SiteExternalId / CompanyId above
TripInfoobjectRequiredThe journey details
↳ ↳ StartDatedatetimeRequired
↳ ↳ EndDatedatetime?Optional
↳ ↳ CodestringOptional
↳ ↳ StatusId, PromotionalStatusId, PackageDepartureStatusIdint?OptionalReference existing lookups if used
↳ ↳ AssetIdguid?OptionalTrain assigned to this journey
↳ ↳ JourneyToFromIdint?OptionalDirection/leg indicator for the journey
↳ ↳ OccupationValueint?OptionalOccupancy figure for the journey
TripStoppagesarrayOptionalIntermediate stops for this journey
↳ ↳ RouteStoppageIdguidRequiredMust reference an existing stoppage on this journey's route - the routeStoppageId returned by SaveRouteInfo's RouteStoppages response
↳ ↳ DatedatetimeRequiredTogether with RouteStoppageId this is the match key - Id is not used to find an existing trip stoppage
↳ ↳ ItineraryCustomizedboolOptionalDefault false. We confirmed in the save logic: when false, ItineraryItems below is ignored and the itinerary is auto-cloned from the route stoppage instead; when true, exactly what you send in ItineraryItems is saved
↳ ↳ ItineraryItemsarrayOptionalOnly takes effect when ItineraryCustomized is true
↳ ↳ ↳ Name, DescriptionstringOptionalNo Code field at this level (unlike the route-level itinerary item)
↳ ↳ ↳ OrderintOptional
↳ ↳ ↳ DeletedboolOptionalDefault false
↳ ↳ ↳ ExternalIdentifierstringOptionalYour own key for this specific itinerary item, so you can update it later without knowing its internal id
↳ ↳ ↳ Id-Auto-setDon't send
If you send TripStoppages at all, it replaces the full set of stoppages on the journey - any previously saved for a RouteStoppageId/Date combination left out of the list is removed. When a stoppage is customized, its ItineraryItems follow the same full-replacement rule.
POST/v1/integration/transportation/SaveTripPartsAttach coaches to a journey
FieldTypeRequired?Notes
TripExternalIdstringConditionalRequired unless TripInfoId is given
TripInfoIdguidConditionalInternal id alternative to TripExternalId
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartExternalIdsarray of stringConditionalRequired (non-empty) unless PartIds is given
PartIdsarray of guidConditionalInternal ids alternative to PartExternalIds
This call replaces the full set of coaches on the journey, not just the ones you send. Any coach attached previously that is left out of the list on a later call is automatically removed from the journey - and any suites/seats already published for that coach on this journey (via SaveTripCompartments) are removed with it. Always send the complete current list of coaches for this journey.
POST/v1/integration/transportation/SaveTripCompartmentsPublish a suite/seat onto a journey
FieldTypeRequired?Notes
TripExternalIdstringConditionalRequired unless TripInfoId is given
TripInfoIdguidConditional
CompartmentExternalIdstringConditionalRequired unless CompartmentId is given
CompartmentIdguidConditional
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
CompartmentTypeIdintOptional
TelephoneExtensionNumberstringOptional
POST/v1/integration/transportation/SaveTripPassengerAssign a passenger/booking to a seat on a journey
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key for this booking
TripExternalIdstringConditionalRequired unless TripPassenger.TripInfoId is already set
CompartmentExternalIdstringConditionalRequired unless TripPassenger.CompartmentId is already set
PassengerExternalIdstringConditionalRequired unless TripPassenger.PassengerId is already set
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note. Also used to save the auto-created Passenger below
TripPassengerobjectRequired
BookingNumber, AgentstringOptionalSending an empty string leaves the existing value unchanged on updates rather than clearing it
ReservedBystringOptionalEmail address of the person who reserved the booking. Stored as free text exactly as you send it (trimmed) - it does not need to match a Ketendo user or Member. Behaves like Agent: sending an empty string leaves the existing value unchanged on updates rather than clearing it
CheckedInbool?OptionalUnlike Agent/ReservedBy, a value you send here does overwrite what's saved. Omitting it leaves the existing value untouched on an update, and leaves it null on a create - null and false both mean "not checked in". Once a passenger is checked in inside Ketendo this field is locked to the integration - see the callout below
CheckedInDate-Auto-setWe verified this is never read from the request - it's always computed from CheckedIn (set to the current time when true, cleared when false)
HasUpdatedFieldsbool?OptionalInternal sync flag - only relevant if your integration also interacts with the Ketendo mobile app's offline sync for this booking
PassengerobjectConditionalOnly required if PassengerExternalId hasn't been seen before - used to auto-create the passenger in the same call. Takes the same fields as SavePassenger, including DateOfBirth and GenderId
TripInfoId, CompartmentId, PassengerId, Id-Auto-setResolved from the ExternalIds above
Breaking change - action needed. The old ReservedByEmail field (and TripPassenger.ReservedById) have been removed. Send the email on TripPassenger.ReservedBy instead. If you keep sending ReservedByEmail the call will still succeed but the field will silently stop being saved, because unknown properties are ignored rather than rejected - so please rename it in your payload.
ReservedBy does not have to match a Ketendo user, so you do not need to save a Member before saving a trip passenger on account of this field.
Check-in is protected. If CheckedIn is already true in Ketendo, this endpoint ignores CheckedIn and CheckedInDate on your payload - a physical check-in is captured on the Portal, and a source system can't undo or re-date it. Everything else in the same payload still saves normally and the call still returns success, so you can keep re-sending a booking unchanged. While the passenger is not checked in, sending true or false works as you'd expect.
If the passenger doesn't exist yet, you can skip a separate SavePassenger call and include the full Passenger object inline here instead - it will be created automatically.
POST/v1/integration/transportation/SaveTripMemberAssign crew/staff to a journey
FieldTypeRequired?Notes
ExternalIdstringRequiredYour unique key for this assignment
TripExternalIdstringConditionalRequired unless TripInfoId is given
TripInfoIdguidConditional
MemberExternalIdstringOptionalIf provided, must already resolve to an existing Member (crew person) - this API does not create Members
MemberEmailstringOptionalAlternative to MemberExternalId/TripMember.MemberId - if you only have the crew Member's email, send it here instead and we resolve it for you, scoped to CompanyId. Ignored if either of the other two is already set. The call fails with a clear error if the email doesn't match a Member on your company
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
TripMemberobjectRequired
BookingNumberstringOptional
IsProposedboolOptionalDefault false
ProductIdguidRequiredMust reference an existing Product
StartDate, EndDate, CheckedIn-Optional
CheckedInDate-Auto-setWe verified this is never read from the request - it's always computed from CheckedIn (set to the current time when true, cleared when false)
TripInfoId, MemberId, ExternalIdentifier-Auto-set

Optional endpoints

Only needed if you're tracking part maintenance or want to bulk-assign amenities.

POST/v1/integration/transportation/SavePartOperationStatusMaintenance/operation status for a part
FieldTypeRequired?Notes
ExternalIdstringRequired
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartOperationStatusobjectRequiredName/Code/Description/Order/Colour/TextColour all Optional (Order only takes effect from your second call onward - see note on the lookups above); Id Auto-set
POST/v1/integration/transportation/SavePartOperationLog a maintenance record/operation against a part
FieldTypeRequired?Notes
ExternalIdstringRequired
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
PartOperationobjectRequired
PartIdguid?OptionalPart this operation relates to
StartTime, EndTimedatetime?OptionalLeaving these out on an update keeps the existing value
OperationStatusIdint?OptionalLeaving this out on an update keeps the existing value
Id-Auto-setDon't send
There is no OperationType field here - we checked and it isn't persisted by this endpoint, so it's been removed from this table. Everything else listed above is genuinely saved.
POST/v1/integration/transportation/SavePartFacilitiesBulk-assign amenities to a coach
FieldTypeRequired?Notes
PartExternalIdstringRequiredThe coach to update
CompanyIdintRequiredWhich company this record belongs to - see the CompanyId note
FacilityExternalIdsarray of stringRequiredAt least one facility, all must already exist (save with SaveFacility first)
WindowTypeId, WheelTypeId, ToiletTypeIdint?OptionalAlso set these on the same coach in one call - leaving one out (or sending null) clears that value on the coach, it does not leave the existing value unchanged
FacilityExternalIds replaces the full set of facilities on the coach, not just the ones you send - always send the complete current list of facilities for this coach.

Reconciling passengers in bulk

Reconciling in bulk

The lookups above fetch one record at a time. If you need the whole set - for example to work out which of your passengers we already have before running a delta - use the endpoint below instead of the full list endpoints. It returns only the two fields needed to match records up, projected directly in the database, so no related data is loaded and the response stays small.

POST/v1/integration/transportation/PassengerExternalIdsAll passenger ids + your ExternalIds
FieldTypeRequired?Notes
CompanyIdintRequiredWhich company's passengers to return
OnlyMappedboolOptionalDefaults to true, returning only passengers that actually carry an ExternalIdentifier - which is what you want for reconciliation. Send false to include passengers created in Ketendo that have never been mapped to your system
IncludeDeletedboolOptionalDefaults to false. Note this is the opposite default to our older list endpoints
PageNumber, PageSizeintOptionalOmit both (or send 0) to get every record in one response - see the size note below. Send both to page
This is deliberately narrow: the database returns just the two columns and nothing else - no contact details, no trips, no nested objects. At roughly 41 000 passengers the whole set is only a few MB, so requesting it unpaged is safe and well inside the limits of tools like Logic Apps.
Don't use /v1/Transportation/PassengersList for this. That endpoint returns fully-populated passengers including every trip each one has been on, which for a full company can run to well over 100 MB and will fail in Logic Apps with a buffer error. If you do use it, always send PageNumber and PageSize - leaving them out returns every record.
Worth checking whether you need this at all: SavePassenger is an upsert keyed on your ExternalId, so you can simply send a passenger and we'll create or update as appropriate - no existence check needed. This endpoint is for when you want to compute the difference on your side first.