> ## Documentation Index
> Fetch the complete documentation index at: https://developer.every.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Идемпотентные запросы

API поддерживает идемпотентность операций через заголовок `Idempotency-Key`. Это позволяет безопасно повторять запросы при сетевых сбоях или таймаутах, не создавая дублирующих операций.

## Обязательные операции

Операции создания бронирований требуют обязательного заголовка `Idempotency-Key`:

* `POST /v2/b2b/booking/lounges` — бронирование бизнес-зала
* `POST /v2/b2b/booking/fast-tracks` — бронирование фаст-трека

## Опциональные операции

Операции подтверждения и отмены бронирований идемпотентны по своей природе через бизнес-логику (повторный вызов возвращает `409` при недопустимом состоянии) и не требуют обязательного ключа идемпотентности. Однако рекомендуется передавать `Idempotency-Key` для упрощения идемпотентных ретраев при сетевых сбоях:

* `POST /v2/b2b/booking/lounges/{bookingID}/confirm` — подтверждение брони бизнес-зала
* `POST /v2/b2b/booking/lounges/{bookingID}/cancel` — отмена брони бизнес-зала
* `POST /v2/b2b/booking/fast-tracks/{bookingID}/confirm` — подтверждение брони фаст-трека
* `POST /v2/b2b/booking/fast-tracks/{bookingID}/cancel` — отмена брони фаст-трека

## Формат ключа идемпотентности

`Idempotency-Key` — это строка длиной до 256 символов. Рекомендуется использовать UUID для обеспечения глобальной уникальности.

**Пример:**

```
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
```

## Поведение (Source-of-Truth)

При повторном запросе с тем же `Idempotency-Key`:

### Совпадение тела запроса

Если тело запроса совпадает с сохранённым при первом использовании ключа:

* Сервер возвращает **строго тот же HTTP-код и тело ответа**, что были возвращены при первом запросе
* Заголовок `Idempotency-Status: reused` указывает на повторное использование результата
* Операция не выполняется повторно

### Несовпадение тела запроса

Если тело запроса отличается от сохранённого при первом использовании ключа:

* Сервер возвращает `409 IdempotencyConflict`
* Операция не выполняется

## Правила сравнения тела запроса

Для определения конфликта сравниваются следующие поля:

* **POST /v2/b2b/booking/lounges**: все поля `LoungeBookingRequest`:
  * `lounge_id`
  * `first_name`
  * `last_name`
  * `email`
  * `phone`
  * `calling_code`
  * `transport_number`
  * `guests`

* **POST /v2/b2b/booking/fast-tracks**: все поля `FastTrackBookingRequest`:
  * `fast_track_id`
  * `first_name`
  * `last_name`
  * `email`
  * `phone`
  * `calling_code`
  * `transport_number`
  * `guests`

Сравнение выполняется путём нормализации JSON (игнорирование порядка полей, нормализация пробелов) и побайтового сравнения нормализованных представлений.

## TTL и истечение срока действия

Ключ идемпотентности имеет TTL **72 часа** с момента первого использования.

После истечения TTL:

* Ключ считается недействительным
* Новый запрос с тем же ключом обрабатывается как новый запрос (создаётся новая операция)
* Возвращается `Idempotency-Status: created`
* **Не** возвращается ошибка `422` или `409`

## Заголовки ответа

Все операции, поддерживающие идемпотентность, возвращают:

* `Idempotency-Key`: эхо переданного ключа
* `Idempotency-Status`:
  * `created` — операция создана впервые
  * `reused` — операция повторно использована (возвращён сохранённый результат)

## Примеры использования

### Успешное создание бронирования

**Первый запрос:**

```http theme={null}
POST /v2/b2b/booking/lounges HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "lounge_id": "123e4567-e89b-12d3-a456-426614174000",
  "first_name": "Ivan",
  "last_name": "Ivanov",
  "guests": [
    {
      "first_name": "Ivan",
      "last_name": "Ivanov",
      "type": "Adult",
      "lead": true
    }
  ]
}
```

**Ответ:**

```http theme={null}
HTTP/1.1 202 Accepted
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Idempotency-Status: created
Location: /v2/b2b/booking/lounges/789e4567-e89b-12d3-a456-426614174000

{
  "booking_id": "789e4567-e89b-12d3-a456-426614174000",
  "status": "Processing",
  "created_at": "2025-11-08T06:00:00Z"
}
```

### Повторный запрос с тем же ключом и телом

**Повторный запрос (идентичный первому):**

```http theme={null}
POST /v2/b2b/booking/lounges HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "lounge_id": "123e4567-e89b-12d3-a456-426614174000",
  "first_name": "Ivan",
  "last_name": "Ivanov",
  "guests": [
    {
      "first_name": "Ivan",
      "last_name": "Ivanov",
      "type": "Adult",
      "lead": true
    }
  ]
}
```

**Ответ (тот же, что и при первом запросе):**

```http theme={null}
HTTP/1.1 202 Accepted
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Idempotency-Status: reused
Location: /v2/b2b/booking/lounges/789e4567-e89b-12d3-a456-426614174000

{
  "booking_id": "789e4567-e89b-12d3-a456-426614174000",
  "status": "Processing",
  "created_at": "2025-11-08T06:00:00Z"
}
```

### Конфликт идемпотентности

**Запрос с тем же ключом, но другим телом:**

```http theme={null}
POST /v2/b2b/booking/lounges HTTP/1.1
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "lounge_id": "123e4567-e89b-12d3-a456-426614174000",
  "first_name": "Petr",  // Изменено имя
  "last_name": "Ivanov",
  "guests": [
    {
      "first_name": "Petr",
      "last_name": "Ivanov",
      "type": "Adult",
      "lead": true
    }
  ]
}
```

**Ответ:**

```http theme={null}
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "code": "IdempotencyConflict",
  "message": "Idempotency Key используется с другими параметрами",
  "request_id": "xxxx"
}
```

## Рекомендации

1. **Генерация ключей**: Используйте UUID v4 для генерации уникальных ключей идемпотентности
2. **Хранение ключей**: Сохраняйте ключи идемпотентности вместе с результатами операций для возможности повторного использования
3. **Обработка ошибок**: При получении `409 IdempotencyConflict` проверьте, не изменились ли параметры запроса
4. **TTL**: Учитывайте, что ключи действительны только 72 часа — после истечения срока используйте новый ключ
