# How to Use TREK's Booking Import Feature with KDE Itinerary for PDF and Email Extraction

> Learn how to use TREK's booking import feature with KDE Itinerary. Effortlessly extract travel details from PDFs and emails into structured reservations. Streamline your travel planning today.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: how-to-guide
- Published: 2026-07-11

---

**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`](https://github.com/mauriceboe/TREK/blob/main/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`](https://github.com/mauriceboe/TREK/blob/main/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`](https://github.com/mauriceboe/TREK/blob/main/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`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/booking-import/kitinerary.types.ts).

### Mapping Layer

[`kitinerary-mapper.ts`](https://github.com/mauriceboe/TREK/blob/main/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.

1. **Upload and Validation** – The frontend sends a `multipart/form-data` POST request. `BookingImportController.validateImport` verifies write access to the trip, validates the import mode (`no-ai`, `fallback-on-empty`, or `force-ai`), and checks file types and size limits.

2. **KItinerary Extraction** – If the mode is not `force-ai` and the binary is present, `KitineraryExtractorService.extract` executes `kitinerary-extractor` against the temporary file. The binary returns a JSON-LD array of reservations.

3. **LLM Fallback** – When the mode is `fallback-on-empty` or `force-ai`, `LlmParseService` processes the document using a language model to produce equivalent `KiReservation` objects.

4. **Data Mapping** – `mapReservations` iterates over the extraction results, converting each `KiReservation` into a `ParsedBookingItem`. It normalizes ISO-8601 dates using the `toIsoString` helper and resolves IATA codes to full airport metadata.

5. **Preview Response** – The service returns a list of `ParsedBookingItem` objects and any extraction warnings. Items derived from AI automatically include `needs_review: true` to flag them for user verification.

6. **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.

7. **Persistence** – `BookingImportService.confirm` auto-creates venue places via `createPlace`, geocodes missing coordinates using `searchNominatim`, resolves check-in/out days through `resolveDayId`, and finally persists the reservation with `createReservation`. 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_PATH` environment variable
- The system library path at `/usr/lib/*/libexec/kf6/kitinerary-extractor`
- The system `PATH` for an executable named `kitinerary-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:

```bash
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:

```json
{
  "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:

```bash
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:

```json
{
  "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:

```bash
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:

```json
{ "jobId": "8a4f5c2d-c3b1-4e7a-9a23-f1e2b9d7c0e1" }

```

Poll the job status:

```bash
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 to `LlmParseService` for 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 `KitineraryExtractorService` to 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.findByIata` during mapping, and deferred geocoding via `searchNominatim` during confirmation.
- All source files are located in `server/src/nest/booking-import/` with type definitions in [`kitinerary.types.ts`](https://github.com/mauriceboe/TREK/blob/main/kitinerary.types.ts) and schemas in [`shared/src/reservation/reservation.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/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`](https://github.com/mauriceboe/TREK/blob/main/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.