> ## 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.

# Statuses and errors

> What each status of a flight AI search task means, why a search fails, and how to handle each HTTP status of the four routes.

A flight AI search answer has two layers. The HTTP status says whether the request was accepted. When it was, the `status` field says where the search is. Check the HTTP status first, then `status`.

## Task status

Every successful answer (`200` or `202`) carries `status`, on `POST /v1/flight_ai_search`, on the task read and on `:cancel`.

| `status` | Meaning | What to do |
| - | - | - |
| `working` | The search is running. A `202` always carries this status. | Read the task again after `Retry-After` seconds, or [stream its events](https://docs.dev.gojinko.com/ai/flight-search/waiting-for-results.md#stream-the-events). |
| `input_required` | The reply asks the traveler a question; `question` says what is missing. The task waits for the answer. | Show `message` and any `question_options`, then send the traveler's answer with the same `context_id`. See [Answer a question](https://docs.dev.gojinko.com/ai/flight-search/conversations.md#answer-a-question). |
| `completed` | The turn ended. `options` and `groups` hold the results. `options` is empty when nothing matched or the request was out of scope; a `per_destination` group still lists each destination with its `empty_reason`. | Show `message` and the options. |
| `failed` | The turn could not finish; `error` says why. | See [Why a search fails](#why-a-search-fails). |
| `canceled` | The task was canceled, or a later message replaced it; then `superseded_by` names the new task. | Read the task in `superseded_by`, if any. |

On `POST /v1/flight_ai_search` without streaming, a `200` always carries one of the last four statuses: the turn is over, and nothing more arrives for it. The task read answers `200` with any status, `working` included, so keep reading it while `status` is `working`.

A `completed` reply can still ask the traveler something, with no `question` field, for example `Changing a date, an airport or a filter may find some.` when no fare meets every filter, or `Tell me where from, where to and when.` for a message that is not a flight request. Always show `message`. The traveler's reply goes in the same conversation.

When part of the answer could not be searched, the turn still ends `completed`, with the options found and a `not_searched` note for the rest. See [When nothing matches](https://docs.dev.gojinko.com/ai/flight-search/conversations.md#when-nothing-matches).

In the event stream, `task.status` events also carry `submitted`, the moment a turn is accepted. A task read shows that moment as `working`.

## Why a search fails

A failed task answers `200` with `status: "failed"` and an `error`:

```json theme={null}
{
  "context_id": "ctx_mvq3e2f1d8b04a6c2e9f7135",
  "task_id": "atk_mvq3e2f97a1c4d0b8e6f2359",
  "status": "failed",
  "error": { "code": "deadline_exceeded", "message": "The search took too long. Please send the message again." }
}
```

| `error.code` | Cause | `error.message` | What to do |
| - | - | - | - |
| `understanding_failed` | The message could not be read as a flight search. | `The message could not be understood. Please rephrase it.` | Ask the traveler to rephrase. |
| `deadline_exceeded` | The turn did not end within 5 minutes. | `The search took too long. Please send the message again.` | Send the message again. |
| `search_refused` | The search refused the request. | `The search could not be completed. Please try again.` | Send the message again; contact support if it repeats. |
| `agent_error` | An error stopped the turn. | `The search could not be completed. Please try again.` | Send the message again. |
| `max_attempts_exceeded` | The turn was stopped after repeated errors. | `The search could not be completed. Please try again.` | Send the message again. |
| `context_expired` | The task was waiting for an answer when its conversation expired (24 hours without a message). | `The search could not be completed. Please try again.` | Start a new conversation without `context_id`. |

`message` is safe to show to the traveler. Branch on `code`.

The JSON answer of a failed turn can carry `charge`, for what the turn used before it stopped.

## HTTP statuses

Errors use the Jinko error format: `{"error": {"code": "...", "message": "..."}}`. See [Errors](https://docs.dev.gojinko.com/concepts/errors.md).

### All four routes

| HTTP | `error.code` | When | What to do |
| - | - | - | - |
| `400` | `BAD_REQUEST` | Malformed JSON (`Malformed JSON in request body`), or an invalid header such as a reserved `X-End-User-Id`. | Fix the request. |
| `401` | `AUTH_REQUIRED` | No key (`Missing X-API-Key or Authorization header.`), a key that is not a `jnk_` key, or an invalid or expired key (`Invalid or expired API key.`). | Send a valid key; see [API keys](https://docs.dev.gojinko.com/authentication/api-keys.md). |
| `403` | `FORBIDDEN` | The key is valid but is not a tenant API key (`jnk_t_…`), or may not run live searches, for example `This API key is not granted the "search_live" scope required by this endpoint`; or AI search is not enabled for the tenant: `Travel AI search is not enabled for this tenant.` | Use a tenant key and check its scopes in the dashboard. The key is valid, so do not rotate it. |
| `422` | `BAD_REQUEST` | A field or header is out of range; `message` names it. | Fix the field. |
| `429` | `RATE_LIMITED` | The key has used up its request allowance (`The API key has used up its request allowance`). | Retry later; see [Rate limits](https://docs.dev.gojinko.com/concepts/rate-limits.md). |
| `502` | `UPSTREAM_ERROR` | The request could not be completed (`The request could not be completed.`). | Retry. On `POST`, retry with the same `Idempotency-Key`. |
| `503` | `TEMPORARILY_UNAVAILABLE` | The search is briefly unavailable. | Retry after a few seconds, with the same `Idempotency-Key` on `POST`. |
| `504` | `UPSTREAM_TIMEOUT` | No answer in time (`The request timed out before a result was available. Retry the request.`). | Retry with the same `Idempotency-Key`; the first message may already be running. |

### `POST /v1/flight_ai_search`

| HTTP | When |
| - | - |
| `200` | The turn ended within the wait: `completed`, `input_required`, `failed` or `canceled`. With `Accept: text/event-stream`, the turn as a stream. A retry with an `Idempotency-Key` whose turn is over also answers `200` with its stored answer. |
| `202` | The search is still running at the end of the wait: `status: "working"`, with `Location`, `Retry-After` and, when you sent `Prefer`, `Preference-Applied`. |
| `402` | `PAYMENT_REQUIRED`: your organization's balance is below what a turn needs. `message` says how much and carries the top-up link. |
| `404` | `NOT_FOUND`, `No such conversation or task.`: the `context_id` is unknown or belongs to another key's tenant, or the `task_id` is not in that conversation. |
| `410` | `GONE`, `This conversation has expired. Start a new one without context_id.`: no message for 24 hours. |
| `422` | The body is out of range. `text` must be 1 to 2000 characters; `timezone` is required and must be an IANA time zone; `currency` must be 3 letters (`currency: an ISO 4217 code`); `task_id` needs `context_id` (`task_id needs the context_id of its conversation`). Also `Idempotency-Key` outside 1 to 200 characters, and a body sent without a JSON `Content-Type`. |

### `GET /v1/flight_ai_search/tasks/{task_id}`

| HTTP | When |
| - | - |
| `200` | The task, with its current `status`. |
| `404` | `NOT_FOUND`, `No such task.`: the task is unknown or belongs to another tenant. |
| `422` | `task_id` is not 1 to 100 characters. |

A task stays readable after its conversation expires.

### `GET /v1/flight_ai_search/tasks/{task_id}/events`

| HTTP | When |
| - | - |
| `200` | The event stream. |
| `404` | `NOT_FOUND`, `No such task.` |
| `422` | `task_id` is not 1 to 100 characters, or `Last-Event-ID` is longer than 20 characters. |

### `POST /v1/flight_ai_search/tasks/{task_id}:cancel`

| HTTP | When |
| - | - |
| `200` | The task was `working` or `input_required` and is now `canceled`. |
| `404` | `NOT_FOUND`, `No such task.` |
| `409` | `CONFLICT`: the task has already ended, for example `The task has already ended (completed).` |
| `422` | `task_id` is not 1 to 100 characters. |

## Related

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


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