> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dev.gojinko.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversations

> Answer the questions a flight AI search asks, refine a search with follow-ups, change it while it runs, and know when a conversation expires.

A flight AI search is a conversation. The traveler can answer a question, add a condition, change a date or an airport, or ask for more options, and each message builds on what was said before.

## Conversations and tasks

| Id | Prefix | What it is |
| - | - | - |
| `context_id` | `ctx_` | The conversation with one traveler. It keeps the conditions of the search so far: places, dates, travelers, filters, the question asked and the last search. |
| `task_id` | `atk_` | One search in that conversation, with its own `status`. You read, stream and cancel a search by its `task_id`. |

The first message, sent without `context_id`, starts a conversation. Its answer carries both ids. Send `context_id` with every later message of the same traveler; keep one per traveler and conversation.

Where a later message goes depends on the conversation's open task:

| When the message arrives | What happens |
| - | - |
| A task is waiting for an answer (`input_required`). | The message answers it. The same task continues, with the same `task_id`. |
| A task is still searching (`working`). | That task is canceled, with `superseded_by` set to a new task that handles the message. |
| No task is open. | A new task starts and keeps the conditions of the conversation. |

You do not need to send `task_id`. When you do, it must belong to the conversation, and it needs `context_id`: `task_id` alone answers `422`, and a task of another conversation answers `404`.

## Answer a question

When a search cannot start, the answer is `input_required`. `message` is the question to show, and `question` says what is missing. Some questions carry `question_options`, the choices to show:

```json theme={null}
{
  "context_id": "ctx_mvq3c1d70b9e4f2a6c813d57",
  "task_id": "atk_mvq3c1df5d20a8e7c49b16f2",
  "status": "input_required",
  "message": "Which San Jose do you mean?",
  "question": "which_place",
  "question_options": [
    { "value": "SJC", "label": "San Jose, United States", "detail": "US" },
    { "value": "SJO", "label": "San José, Costa Rica", "detail": "CR" }
  ],
  "criteria": {
    "trip_type": "oneway",
    "origins": [{ "code": "FRA", "type": "city", "name": "Frankfurt", "country_code": "DE" }],
    "destinations": ["..."],
    "passengers": { "adults": 1, "children": 0, "infants": 0 },
    "currency": "USD"
  },
  "notes": [],
  "charge": { "turn": 1, "llm_usd": 0.0053, "api_usd": 0, "total_usd": 0.0053, "currency": "USD" }
}
```

| `question` | Asked when | Example `message` |
| - | - | - |
| `origin` | There is no origin. | `Where are you flying from?` |
| `which_place` | A place name fits several places, no place matches it, or no city of a country can be chosen. `question_options` lists the places it could be, when there are some. | `Which San Jose do you mean?`, `Which city in Thailand do you want to search? Please name the city or airport.` |
| `dates` | A date does not exist, a date has passed, the return is before the departure, or a flight of a multi-city trip has no date. | `That date has passed. Which day do you want to fly?`, `The return is before the departure. Which dates do you mean?`, `Which day is each flight of the trip?` |
| `passengers` | A family or a group travels and its adults, children and infants are not stated, or the count is not possible. | `Who is travelling: how many adults, children (2–11) and infants (under 2)?` |
| `group_too_large` | More travelers than one search takes: more than 9 with a seat (adults and children), more than 8 children, more than 4 infants, or more infants than adults. | `One search takes at most 9 travellers with a seat, 8 children and no more infants than adults. How would you like to split the group?` |
| `contradiction` | The origin and the destination are the same place, or the request asks for a direct flight with a stopover. | `You asked for a direct flight and a stopover. Which one do you want?` |

A request that names no destination is not asked for one: the answer covers several destinations from the origin instead. Always show `message`, whatever the value of `question`.

When several things are missing, the search asks for one at a time. A question runs no search, so its `api_usd` is `0`.

Send the traveler's answer as `text`, with the same `context_id`. With `question_options`, send the chosen `value`:

```bash theme={null}
curl https://api.gojinko.com/v1/flight_ai_search \
  --header "X-API-Key: $JINKO_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"text": "SJO", "timezone": "Europe/Berlin", "context_id": "ctx_mvq3c1d70b9e4f2a6c813d57"}'
```

The same task continues, with the same `task_id`, and searches. While the search runs, the answer is `202` with the early reply:

```json theme={null}
{
  "context_id": "ctx_mvq3c1d70b9e4f2a6c813d57",
  "task_id": "atk_mvq3c1df5d20a8e7c49b16f2",
  "status": "working",
  "message": "Searching flights: Frankfurt → San José, 10 Nov 2026, one way, 1 adult.",
  "criteria": { "trip_type": "oneway", "origins": ["..."], "destinations": ["..."], "departure": { "date": "2026-11-10" }, "passengers": { "adults": 1, "children": 0, "infants": 0 }, "currency": "USD" },
  "notes": [{ "kind": "default_passengers", "text": "No travellers were given; priced for one adult." }]
}
```

You do not send `question` back: the conversation knows what it asked. The traveler can also answer with a whole new request; it is read the same way.

## Refine the search

Each follow-up answers with a new search that keeps the conversation's conditions and applies the change. "Show me more" is the exception: it returns more options of the last search.

| The traveler says | What changes |
| - | - |
| "nonstop only", "business class", "with a checked bag", "under 400 EUR" | The condition is added, or replaces the earlier value of the same kind. Places, dates and travelers are kept. |
| "make it Porto instead", "from Orly" | That side of the route changes. The other side, the dates, the travelers and the filters are kept. |
| "a week later", "back on the 20th" | The dates change. A new return alone keeps the departure. |
| "we are 2 adults and a child of 4" | The travelers change. |
| "any airline is fine", "forget the budget" | The condition is removed. Airlines, stops, budget, cabin, checked bag, refundable, changeable, times, connection airports and avoided airports can be removed. |
| "the fastest one", "fewest connections" | The order of the options changes (`priority`). |
| "price March 2" | Options for that day, as a new date. |
| "show me more" | The next options of the last search, up to 5, while their fares are valid: `5 more options.` When none are left: `There are no more options for this search.` |

`has_more: true` means more options of the last search are available; ask for more in the same conversation. There is no page parameter.

When the fares of the remaining options have expired, "show me more" answers with fresh options of the same search. The reply adds `The earlier prices had expired; these are searched again now.`, `notes` carries `repriced`, and the new fares have new tokens and a new `expires_at`.

### Notes

`notes` lists the assumptions made, what could not be done, and what limits the answer. Most notes carry a `text` to show; `message` often says the same.

| `kind` | Fields | Meaning |
| - | - | - |
| `default_passengers` | `text` | `No travellers were given; priced for one adult.` |
| `passengers_assumed_adults` | `text` | `4 travellers were taken as 4 adults; say if any are children (2–11) or infants.` |
| `default_window` | `text` | `No dates were given; the next 90 days were searched.` |
| `default_stay` | `stay_days`, `text` | `No return date or trip length was given; a 7-day stay was searched.` The trip is a holiday, or was asked as a round trip, without a return or a length. |
| `no_fares` | `text` | `Only the first day of the window was searched.` |
| `shops_capped` | `cap`, `candidates` | Not every candidate day or destination was searched: `cap` is the number searched, `candidates` the number of candidates. Name a day or a destination in the same conversation to get options for it. |
| `not_searched` | `origin`, `destination`, `departure`, `reason` | No options could be returned for that route and date (`reason`: `timeout`, `failed` or `refused`). The rest of the answer stands. |
| `not_supported` | `capability`, `text` | With `capability: "award"`: `Miles and points are not searched; these are cash fares.` |
| `monitoring_not_set_up` | `text` | `Price tracking is not set up by this search; these are the prices at the time of the search.` The request is answered like any other. |
| `beyond_booking_horizon` | `horizon` | A date is more than one year ahead (after `horizon`); nothing was searched. |
| `out_of_scope` | `capability` | The message asks for something this search does not do. See [When nothing matches](#when-nothing-matches). |
| `repriced` | `text` | `The earlier prices had expired; these are searched again now.` |

## Change the search while it runs

A message sent while a task is `working` replaces it. Reading the first task then shows:

```json theme={null}
{
  "context_id": "ctx_mvq3d4e81c6b0a9f2e7d5341",
  "task_id": "atk_mvq3d4ef6e1b92c0a4d8f735",
  "status": "canceled",
  "superseded_by": "atk_mvq3d5a2b7c4e1f09d3a6e28"
}
```

Follow the task in `superseded_by`. To stop a search without sending a new message, [cancel it](https://docs.dev.gojinko.com/ai/flight-search/waiting-for-results.md#cancel-a-search).

## When nothing matches

A search that finds no fare within the conditions ends `completed`, with no option and a reply that says so, for example `No flight was found for Paris → Porto, 10 May 2027, back 17 May 2027, 1 adult, within the conditions asked. Changing a date, an airport or a filter may find some.` No filter is relaxed without the traveler.

When the answer covers several destinations, each destination without an option has a `per_destination` row with `option_id: null` and an `empty_reason`, while the other rows keep their options:

```json theme={null}
{
  "destination": { "code": "OPO", "type": "city", "name": "Porto", "country_code": "PT" },
  "dates": { "departure": "2027-05-10", "return": "2027-05-17" },
  "option_id": null,
  "live_cheapest_total": null,
  "empty_reason": "no_fare_within_filters",
  "summary": "Nothing was found within the filters."
}
```

| `empty_reason` | Meaning |
| - | - |
| `no_fare_within_filters` | No fare for the destination met every filter. |
| `not_searched` | The destination was not searched in this turn: other candidates were searched instead (see `shops_capped`), its search did not finish (a `not_searched` note gives the reason), or no day was found to search. Name a day to search it. |

When a search could not be completed, for example after a timeout, the answer ends `completed` with a `not_searched` note and `The search for Paris → Porto, 10 May 2027, back 17 May 2027 could not be completed. Please send the message again.`

A date more than one year ahead is not searched: the answer ends `completed` with a `beyond_booking_horizon` note, for example `Flights can be booked up to one year ahead (2027-10-26); nothing was searched for 3 Nov 2027, one way.`

A request for something other than a flight search also ends `completed`, with no search, a note `out_of_scope` and a reply that says what to do instead:

| The traveler asks for | `capability` | `message` |
| - | - | - |
| A change or a cancellation of a booking | `post_booking` | `This search finds and prices flights. A booking is changed or cancelled from the booking itself, with its JNK reference.` |
| The status of a flight | `flight_status` | `This search finds and prices flights; it has no flight status.` |
| A booking or a payment | `booking_or_payment` | `This search finds and prices flights. Add a fare to a trip to book and pay for it.` |
| Another transport, such as a train or a ferry | `other_transport` | `This search finds and prices flights only.` |
| Something unrelated to travel | `not_travel` | `This search finds and prices flights. Tell me where from, where to and when.` |

A message that is not a request, such as a greeting or a thank-you, ends `completed` with `This search finds and prices flights. Tell me where from, where to and when.`

## Expiry

A conversation expires 24 hours after its last message.

* A message sent with an expired `context_id` answers `410` `GONE`. Start a new conversation without `context_id`.
* A task that was waiting for an answer ends `failed` with `error.code` `context_expired`.
* Tasks stay readable by `task_id` after the conversation expires.

Fares expire sooner: a `trip_item_token` can be added to a trip until its `expires_at`, at most 75 minutes after the search. See [Book a result](https://docs.dev.gojinko.com/ai/flight-search/overview.md#book-a-result).

## Related

* [Flight AI search](https://docs.dev.gojinko.com/ai/flight-search/overview.md)
* [Waiting for results](https://docs.dev.gojinko.com/ai/flight-search/waiting-for-results.md)
* [Statuses and errors](https://docs.dev.gojinko.com/ai/flight-search/statuses-and-errors.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.