How to Use TREK's Booking Import Feature with KDE Itinerary for PDF and Email Extraction
TREK uses the KDE Itinerary binary (kitinerary-extractor) to parse travel confirmations from PDFs, emails, and other formats into structured reservations, with optional AI fallback and a two-step preview-to-confirm workflow.
TREK is an open-source travel planning platform that automates reservation creation from travel documents. The booking import feature leverages KDE Itinerary's extraction capabilities to turn PDFs, EML files, and PKPass data into fully-fledged trip reservations. This integration eliminates manual data entry by parsing confirmation emails and boarding passes directly into your itinerary.
Architecture Overview
The integration is built around four distinct layers that handle everything from HTTP requests to database persistence.
Controller Layer
The BookingImportController in server/src/nest/booking-import/booking-import.controller.ts exposes the REST endpoints for the import workflow. It accepts multipart file uploads at POST /api/trips/:tripId/reservations/import/booking, validates user permissions, and initiates the extraction process. The controller also manages asynchronous imports via the /booking/async endpoint and handles final confirmation through /booking/confirm.
Service Layer
BookingImportService (located in server/src/nest/booking-import/booking-import.service.ts) orchestrates the entire extraction pipeline. This service coordinates between the KDE Itinerary binary, optional LLM parsing, and TREK's internal data models. It handles the heavy lifting of geocoding endpoints, resolving trip days, and persisting confirmed reservations through createReservation.
Extraction Layer
The KitineraryExtractorService in server/src/nest/booking-import/kitinerary-extractor.service.ts serves as a wrapper around the external kitinerary-extractor binary. When invoked, it writes uploaded files to temporary storage and executes the binary, capturing the JSON-LD output as an array of KiReservation objects defined in server/src/nest/booking-import/kitinerary.types.ts.
Mapping Layer
kitinerary-mapper.ts transforms the raw KDE Itinerary output into TREK's internal ParsedBookingItem format. Based on the @type field (such as FlightReservation or HotelReservation), it routes each object to specialized mappers like mapFlight or mapLodging. This layer also enriches airport data via airportService.findByIata and flags AI-derived content for review.
The Booking Import Workflow
The import process follows a strict seven-step pipeline from file upload to persisted reservation.
-
Upload and Validation – The frontend sends a
multipart/form-dataPOST request.BookingImportController.validateImportverifies write access to the trip, validates the import mode (no-ai,fallback-on-empty, orforce-ai), and checks file types and size limits. -
KItinerary Extraction – If the mode is not
force-aiand the binary is present,KitineraryExtractorService.extractexecuteskitinerary-extractoragainst the temporary file. The binary returns a JSON-LD array of reservations. -
LLM Fallback – When the mode is
fallback-on-emptyorforce-ai,LlmParseServiceprocesses the document using a language model to produce equivalentKiReservationobjects. -
Data Mapping –
mapReservationsiterates over the extraction results, converting eachKiReservationinto aParsedBookingItem. It normalizes ISO-8601 dates using thetoIsoStringhelper and resolves IATA codes to full airport metadata. -
Preview Response – The service returns a list of
ParsedBookingItemobjects and any extraction warnings. Items derived from AI automatically includeneeds_review: trueto flag them for user verification. -
User Confirmation – The frontend POSTs selected items to
/api/trips/:tripId/reservations/import/booking/confirm. The user can edit details, accept valid entries, or reject incorrect extractions. -
Persistence –
BookingImportService.confirmauto-creates venue places viacreatePlace, geocodes missing coordinates usingsearchNominatim, resolves check-in/out days throughresolveDayId, and finally persists the reservation withcreateReservation. WebSocket events broadcast the new reservations to connected clients.
Configuring the KItinerary Binary
TREK locates the kitinerary-extractor binary using a hierarchical search strategy. The system checks for the binary in the following order:
- The path specified by the
KITINERARY_EXTRACTOR_PATHenvironment variable - The system library path at
/usr/lib/*/libexec/kf6/kitinerary-extractor - The system
PATHfor an executable namedkitinerary-extractor
The binary must have read access to temporary files created by TREK and must output valid JSON-LD conforming to the KiReservation interface. Date handling in KDE Itinerary can produce either plain ISO strings or wrapped KiDateTime objects; TREK's mapper normalizes both to simple YYYY-MM-DDTHH:MM format.
API Usage Examples
Preview Upload (Synchronous)
Extract reservations from PDFs and emails before confirming them:
curl -X POST "https://your.trek.instance/api/trips/12345/reservations/import/booking" \
-H "Authorization: Bearer <JWT>" \
-F "files=@/path/to/booking.pdf" \
-F "files=@/path/to/confirmation.eml" \
-F "mode=no-ai"
The response contains a preview of extracted items:
{
"items": [
{
"type": "flight",
"title": "Delta DL123 (JFK → LHR)",
"reservation_time": "2024-10-12T14:30",
"reservation_end_time": "2024-10-13T06:45",
"confirmation_number": "ABCDEF",
"metadata": { "airline": "Delta", "flight_number": "DL123" },
"endpoints": [
{ "role":"from","name":"John F. Kennedy Intl (JFK)","code":"JFK","lat":40.6398,"lng":-73.7789,"timezone":"America/New_York","local_time":"14:30","local_date":"2024-10-12"},
{ "role":"to","name":"London Heathrow (LHR)","code":"LHR","lat":51.4700,"lng":-0.4543,"timezone":"Europe/London","local_time":"06:45","local_date":"2024-10-13"}
],
"needs_review": false,
"source": { "fileName":"booking.pdf","index":0 }
}
],
"warnings": []
}
Confirm Imported Items
Persist the validated items to your trip:
curl -X POST "https://your.trek.instance/api/trips/12345/reservations/import/booking/confirm" \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"type":"flight",
"title":"Delta DL123 (JFK → LHR)",
"reservation_time":"2024-10-12T14:30",
"reservation_end_time":"2024-10-13T06:45",
"confirmation_number":"ABCDEF",
"metadata":{"airline":"Delta","flight_number":"DL123"},
"endpoints":[
{"role":"from","name":"John F. Kennedy Intl (JFK)","code":"JFK","lat":40.6398,"lng":-73.7789,"timezone":"America/New_York","local_time":"14:30","local_date":"2024-10-12"},
{"role":"to","name":"London Heathrow (LHR)","code":"LHR","lat":51.4700,"lng":-0.4543,"timezone":"Europe/London","local_time":"06:45","local_date":"2024-10-13"}
],
"source":{"fileName":"booking.pdf","index":0}
}
]
}'
The response returns the created reservation objects:
{
"created": [
{
"id": 987,
"type": "flight",
"title": "Delta DL123 (JFK → LHR)",
"trip_id": "12345"
}
]
}
Asynchronous Import (Background Jobs)
For large batches or slow extractions, use the async endpoint:
curl -X POST "https://your.trek.instance/api/trips/12345/reservations/import/booking/async" \
-H "Authorization: Bearer <JWT>" \
-F "files=@/path/to/many-files.zip" \
-F "mode=fallback-on-empty"
Response:
{ "jobId": "8a4f5c2d-c3b1-4e7a-9a23-f1e2b9d7c0e1" }
Poll the job status:
curl -X GET "https://your.trek.instance/api/trips/12345/reservations/import/jobs/8a4f5c2d-c3b1-4e7a-9a23-f1e2b9d7c0e1" \
-H "Authorization: Bearer <JWT>"
The result contains status, done, total, and either the preview result or an error message.
Data Mapping and Enrichment
The mapping process transforms KDE Itinerary's generic KiReservation objects into TREK-specific schemas. For flight reservations, mapFlight extracts IATA codes and queries airportService.findByIata to populate latitude, longitude, city names, and timezone data. For non-airport endpoints like train stations or bus stops, the system defers geocoding to the confirmation stage via searchNominatim.
The mapper also handles edge cases in date representation. KDE Itinerary sometimes wraps timestamps in KiDateTime objects; the toIsoString helper normalizes these to standard ISO strings. Each mapped item inherits a source object tracking the original filename and extraction index, enabling precise audit trails.
AI Fallback Modes
TREK supports three extraction strategies controlled by the mode parameter:
no-ai– Uses only the KDE Itinerary binary. If extraction fails or returns empty, the import fails.fallback-on-empty– Attempts KDE Itinerary extraction first. If the binary returns no results or errors, automatically falls back toLlmParseServicefor AI-powered extraction.force-ai– Skips the binary entirely and routes all files through the LLM parser.
Items produced via AI are always marked with needs_review: true in the preview response, ensuring users verify machine-generated data before persistence.
Summary
- TREK's booking import uses
KitineraryExtractorServiceto wrap the KDE Itinerary binary and convert PDFs/EMLs into structured data. - The workflow is split between preview (
BookingImportController.preview) and confirmation (BookingImportService.confirm) to allow user verification. - Three extraction modes (
no-ai,fallback-on-empty,force-ai) provide flexibility for documents that resist standard parsing. - Data enrichment occurs in two phases: immediate airport resolution via
airportService.findByIataduring mapping, and deferred geocoding viasearchNominatimduring confirmation. - All source files are located in
server/src/nest/booking-import/with type definitions inkitinerary.types.tsand schemas inshared/src/reservation/reservation.schema.ts.
Frequently Asked Questions
Where does TREK look for the kitinerary-extractor binary?
TREK searches for the binary in three locations in order: the path defined by the KITINERARY_EXTRACTOR_PATH environment variable, the system library path at /usr/lib/*/libexec/kf6/kitinerary-extractor, and finally the system PATH for an executable named kitinerary-extractor. Ensure the binary is executable and accessible to the TREK server process.
What happens if KDE Itinerary cannot parse a file?
If the binary fails to extract data and you are using fallback-on-empty mode, TREK automatically routes the file to LlmParseService for AI-powered extraction. In no-ai mode, the import fails for that specific file and returns a warning in the response. In force-ai mode, the binary is never called, and the LLM handles all parsing.
Can I import multiple files at once?
Yes. The /booking and /booking/async endpoints accept multiple files in a single multipart/form-data request. Simply append multiple -F "files=@path/to/file" arguments to your curl command or form data. The service processes each file sequentially and aggregates the results into a single preview response.
How does TREK handle timezones and local times?
During the mapping phase in kitinerary-mapper.ts, TREK resolves airport IATA codes to full timezone data using airportService.findByIata. The mapper calculates local_time and local_date for each endpoint based on the airport's timezone. For non-airport locations, geocoding during the confirmation stage determines the timezone if not provided by the extraction source.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →