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

# Создание брони

<Note>
  Согласно требованиям аэропортов и партнеров, для некоторых заказов на бронирование фаст-треков
  потребуется указать имя пассажира, email, номер телефона или номер рейса в информации о бронировании.

  Для получения списка обязательных полей для бронирования по конкретным залам смотрите на атрибуты `required_booking_info`
  в /v2/catalog/fast-tracks
</Note>


## OpenAPI

````yaml POST /v2/b2b/booking/fast-tracks
openapi: 3.1.0
info:
  version: 1.0.0
  title: Every Travel B2B API
  description: >
    Партнёрский B2B API для оформления travel-сервисов: бизнес-залы (lounges),

    фаст-трек (fast-track) и eSIM. Все B2B-эндпоинты доступны после получения

    access token через `POST /oauth/v2/token` и (где требуется) с клиентским

    сертификатом mTLS.


    ## Состав API


    - **Идентификация и токены** — OAuth2 (`client_credentials`), JWKS для
    оффлайн-проверки JWT.

    - **Каталоги** — список бизнес-залов, фаст-треков и тарифов eSIM, доступных
    партнёру.

    - **Бронирование (lounge / fast-track)** — двухшаговый сценарий «создать →
    подтвердить»,
      получение данных прохода (`/pass`), отмена.
    - **Заказ eSIM** — двухшаговый сценарий «создать заказ → подтвердить заказ»,
      по подтверждению возвращается выпущенная eSIM с данными для активации (QR/LPA).
    - **Webhooks** — подписка на события (`booking.status.changed`,
    `pass.status.changed`,
      `esim.package.usage_threshold_reached`, `esim.package.exhausted`, `esim.package.expired`).

    ## Кэширование каталогов


    API поддерживает условные запросы для эндпоинтов каталогов через заголовки
    `ETag`/`If-None-Match` 

    и `Last-Modified`/`If-Modified-Since`. Это позволяет клиентам экономить
    трафик при повторных запросах.


    ### Использование ETag


    При первом запросе сервер возвращает заголовок `ETag` с уникальным
    идентификатором версии ресурса.

    При последующих запросах клиент может передать это значение в заголовке
    `If-None-Match`.

    Если ресурс не изменился, сервер возвращает `304 Not Modified` без тела
    ответа.


    ### Использование Last-Modified


    Сервер также возвращает заголовок `Last-Modified` с датой последнего
    изменения ресурса.

    Клиент может передать это значение в заголовке `If-Modified-Since` при
    следующем запросе.

    Если ресурс не был изменен после указанной даты, сервер возвращает `304 Not
    Modified`.


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


    ```

    # Первый запрос

    GET /v2/catalog/lounges

    → 200 OK

    → ETag: "abc123"

    → Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT


    # Повторный запрос с ETag

    GET /v2/catalog/lounges

    If-None-Match: "abc123"

    → 304 Not Modified

    → ETag: "abc123"

    → Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT

    ```


    ## Идемпотентность


    API поддерживает идемпотентность операций через заголовок
    `Idempotency-Key`. 

    Операции создания бронирований (POST /v2/b2b/booking/lounges, POST
    /v2/b2b/booking/fast-tracks),

    операции выпуска и пополнения eSIM (POST /v2/b2b/fulfilment/esims, POST
    /v2/b2b/fulfilment/esims/{iccid}/topup)

    и операции создания аккаунтов и приложений (POST /v2/admin/accounts, POST
    /v2/admin/accounts/{accountID}/applications)

    требуют обязательного заголовка `Idempotency-Key`.


    Операции подтверждения и отмены бронирований (POST
    /v2/b2b/booking/*/confirm,

    POST /v2/b2b/booking/*/cancel) идемпотентны по своей природе через
    бизнес-логику

    (повторный вызов возвращает 409 при недопустимом состоянии) и не требуют
    ключа идемпотентности.


    ### Поведение (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)

    - **POST /v2/b2b/fulfilment/esims**: все поля `CreateAccountEsimRequest`
    (package_template_id, active_period, validity_period)

    - **POST /v2/b2b/fulfilment/esims/{iccid}/topup**: путь (`iccid`) и все поля
    `CreateAccountEsimRequest` (package_template_id, active_period,
    validity_period)

    - **POST /v2/admin/accounts**: все поля `AdminCreateAccountRequest` (name,
    csr_pem)

    - **POST /v2/admin/accounts/{accountID}/applications**: все поля
    `AdminCreateApplicationRequest` (name, allowed_scopes)


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

    и побайтового сравнения нормализованных представлений.


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


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


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

    - Ключ считается недействительным

    - Новый запрос с тем же ключом обрабатывается как новый запрос (создаётся
    новая операция)

    - Возвращается `Idempotency-Status: created`

    - **Не** возвращается ошибка 422 или 409


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


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

    - `Idempotency-Key`: эхо переданного ключа

    - `Idempotency-Status`: 
      - `created` — операция создана впервые
      - `reused` — операция повторно использована (возвращён сохранённый результат)
     
    ## Webhooks


    API поддерживает отправку webhook-уведомлений для отслеживания жизненного
    цикла бронирований

    и проходов. Это позволяет получать уведомления об изменениях статусов без
    поллинга API.


    ### Управление подпиской


    Партнёр может иметь **не более одной** webhook-подписки на аккаунт.
    Управление осуществляется

    через узкие command-эндпоинты, соответствующие поддерживаемым операциям:


    - `POST /v2/b2b/webhooks` — создание подписки

    - `GET /v2/b2b/webhooks` — список подписок (0 или 1 элемент)

    - `GET /v2/b2b/webhooks/{webhookID}` — получение подписки

    - `POST /v2/b2b/webhooks/{webhookID}/change-url` — изменение URL endpoint

    - `POST /v2/b2b/webhooks/{webhookID}/rotate-secret` — ротация секрета
    подписи

    - `POST /v2/b2b/webhooks/{webhookID}/disable` — приостановить доставку

    - `POST /v2/b2b/webhooks/{webhookID}/enable` — возобновить доставку

    - `DELETE /v2/b2b/webhooks/{webhookID}` — удаление подписки


    При создании подписки указываются:

    - `url` — URL endpoint для приёма webhook-уведомлений (должен быть доступен
    по HTTPS)

    - `secret` — секретный ключ для подписи webhook-уведомлений (HMAC-SHA256)


    Партнёр получает уведомления обо всех поддерживаемых событиях; отдельная
    подписка

    на конкретные типы не требуется.


    ### Формат webhook-уведомлений


    Все webhook-уведомления отправляются в формате JSON и содержат:

    - `event` — тип события

    - `timestamp` — время возникновения события (ISO 8601)

    - `data` — данные события (зависит от типа события)


    ### Поддерживаемые события


    - `booking.status.changed` — изменение статуса бронирования

    - `pass.status.changed` — изменение статуса прохода


    ### Безопасность


    Webhook-уведомления подписываются с использованием HMAC-SHA256. Подпись
    передаётся

    в заголовке `X-Webhook-Signature`.
servers:
  - url: https://api.phoenixpass.space
    description: Production
  - url: http://localhost:8080
    description: Local development
  - url: https://api.sandbox.every.ru
    description: Sandbox
security: []
paths:
  /v2/b2b/booking/fast-tracks:
    post:
      tags:
        - b2b
        - booking
      summary: Бронирование фаст-трека
      operationId: createFastTrackBooking
      parameters:
        - $ref: '#/components/parameters/RequestID'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FastTrackBookingRequest'
      responses:
        '202':
          description: Запрос принят в обработку
          headers:
            Location:
              description: URL созданной брони
              schema:
                type: string
                format: uri
            Idempotency-Key:
              $ref: '#/components/headers/IdempotencyKey'
            Idempotency-Status:
              $ref: '#/components/headers/IdempotencyStatus'
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BookingProcessingResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - MutualTLS: []
          Bearer:
            - booking.fasttrack:write
components:
  parameters:
    RequestID:
      name: X-Request-ID
      in: header
      required: false
      schema:
        type: string
      description: >
        Уникальный идентификатор для трассировки запроса. Не влияет на логику
        исполнения запроса.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: >
        Уникальный ключ идемпотентности запроса (строка длиной до 256 символов),
        TTL 72ч.


        См. раздел ["Идемпотентные запросы"](/api-reference/v2/idempotency)
      schema:
        $ref: '#/components/schemas/IdempotencyKey'
  schemas:
    FastTrackBookingRequest:
      type: object
      required:
        - fast_track_id
        - guests
        - customer_id
        - flight_date
        - flight_number
        - first_name
        - last_name
      description: |
        Данные для бронирования фаст-трек.
      properties:
        fast_track_id:
          $ref: '#/components/schemas/FastTrackID'
        first_name:
          type: string
          description: Имя пассажира. Указывается латинскими буквами
          minLength: 2
          example: Ivan
        last_name:
          type: string
          description: Фамилия пассажира. Указывается латинскими буквами
          minLength: 2
          example: Ivanov
        phone:
          type: string
          description: Номер телефона пассажира
          pattern: ^[0-9]+$
          example: '958989632'
          minLength: 1
          maxLength: 20
        calling_code:
          type: string
          description: >-
            Код страны для номера телефона пассажира. Не указывайте символ + в
            начале.
          example: '7'
          pattern: ^\d{1,4}$
          minLength: 1
          maxLength: 4
        flight_number:
          type: string
          description: Номер авиарейса
        flight_date:
          type: string
          format: date-time
          description: Дата и время авиарейса
          example: '2025-11-08T06:00:00.000Z'
        customer_id:
          type: string
          description: Идентификатор пользователя в системе партнера.
          example: '12312312'
          minLength: 1
        guests:
          type: array
          items:
            $ref: '#/components/schemas/FastTrackPassenger'
    BookingProcessingResponse:
      type: object
      properties:
        booking_id:
          $ref: '#/components/schemas/BookingID'
        status:
          $ref: '#/components/schemas/BookingProcessingStatus'
        created_at:
          type: string
          description: Дата создания бронирования
          format: date-time
    IdempotencyKey:
      type: string
      maxLength: 256
      description: >
        Уникальный ключ идемпотентности запроса. Может быть любой строкой длиной
        до 256 символов.

        Рекомендуется использовать UUID для обеспечения глобальной уникальности.
      example: 550e8400-e29b-41d4-a716-446655440000
    FastTrackID:
      type: string
      description: Идентификатор услуги фаст-трека
      format: uuid
    FastTrackPassenger:
      type: object
      required:
        - type
        - first_name
        - last_name
      properties:
        last_name:
          type: string
          description: Фамилия пассажира. Указывается латинскими буквами
          minLength: 2
          example: Ivanov
        first_name:
          type: string
          description: Имя пассажира. Указывается латинскими буквами
          minLength: 2
          example: Ivan
        type:
          type: string
          description: Тип пассажира
          enum:
            - Adult
            - Child
            - Infant
    IdempotencyStatus:
      type: string
      enum:
        - created
        - reused
    BookingID:
      type: string
      format: uuid
      description: Уникальный идентификатор бронирования
    BookingProcessingStatus:
      type: string
      enum:
        - Processing
        - Success
        - Failed
    Error:
      type: object
      description: |
        Базовый формат ошибки API в соответствии с RFC 9457.
      required:
        - code
        - title
        - status
        - instance
      properties:
        code:
          type: string
          enum:
            - Unauthorized
            - Forbidden
            - ResourceNotFound
            - RateLimitExceeded
            - InternalServerError
            - AlreadyCompanyMember
            - ValidationError
            - BookingNotFound
            - CatalogResourceNotFound
            - CatalogResourceConflict
            - PassNotReady
            - PassServiceTypeMismatch
            - BookingAlreadyConfirmed
            - BookingAlreadyCanceled
            - IdempotencyConflict
            - ContractAlreadyExists
            - WebhookNotFound
            - WebhookAlreadyExists
            - WebhookInvalidState
            - InternalError
            - ServiceUnavailable
            - AmenityResourceConflict
            - EsimNotFound
            - EsimPoolExhausted
            - EsimTopupUnavailable
            - EsimResellerAlreadyLinked
          description: Машиночитаемый код ошибки из реестра.
        title:
          type: string
          description: Краткое человекочитаемое описание типа проблемы.
          example: Phone verification failed
        status:
          type: integer
          description: HTTP статус код ошибки.
          example: 400
          minimum: 100
          maximum: 599
        detail:
          type: string
          description: Детальное описание проблемы для отладки и логирования (опционально).
          example: Phone verification failed
        instance:
          type: string
          format: uri-reference
          description: |
            URI, идентифицирующий конкретный экземпляр проблемы.
            Обычно указывает на путь запроса, который вызвал ошибку.
          example: /v1/identity/oauth/token
        details:
          type: object
          additionalProperties: true
          description: Дополнительная информация об ошибке (опционально).
        request_id:
          type: string
          description: >-
            Идентификатор запроса. Указан в заголовке X-Request-ID
            (опционально).
          example: 123e4567-e89b-12d3-a456-426614174000
  headers:
    IdempotencyKey:
      description: |
        Эхо переданного ключа идемпотентности.
        Возвращается в ответах для операций, поддерживающих идемпотентность.
      schema:
        $ref: '#/components/schemas/IdempotencyKey'
    IdempotencyStatus:
      description: >
        Статус идемпотентности операции:

        - `created` — операция создана впервые

        - `reused` — операция повторно использована (возвращён сохранённый
        результат)
      schema:
        $ref: '#/components/schemas/IdempotencyStatus'
    RetryAfter:
      description: Рекомендуемая задержка перед повтором в секундах
      schema:
        type: integer
        minimum: 1
  responses:
    BadRequest:
      description: Некорректный запрос
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: ValidationError
            title: Некорректный запрос
            status: 400
            instance: /v2/b2b/booking/lounges
            detail: Некорректные параметры запроса
            details:
              field: guests_count
              reason: Количество гостей не может быть больше 10 человек
            request_id: xxxx
    Unauthorized:
      description: |
        Ошибка аутентификации. Возвращается в следующих случаях:

        - **Отсутствует или недействителен клиентский сертификат (mTLS)**: 
          Запрос не содержит валидный клиентский сертификат или сертификат не прошел проверку.

        - **Отсутствует или недействителен Bearer токен**: 
          Заголовок `Authorization` отсутствует, имеет неверный формат, или токен истек/недействителен.

        - **Неверный формат токена**: 
          Токен не является валидным JWT или имеет неверную структуру.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing_mtls:
              summary: Отсутствует клиентский сертификат
              value:
                code: Unauthorized
                title: Unauthorized
                status: 401
                instance: /
                detail: Требуется клиентский сертификат для mTLS аутентификации
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
            invalid_mtls:
              summary: Недействительный клиентский сертификат
              value:
                code: Unauthorized
                title: Unauthorized
                status: 401
                instance: /
                detail: Клиентский сертификат не прошел проверку
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
            missing_bearer:
              summary: Отсутствует Bearer токен
              value:
                code: Unauthorized
                title: Unauthorized
                status: 401
                instance: /
                detail: Требуется Bearer токен в заголовке Authorization
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
            invalid_token:
              summary: Недействительный или истекший токен
              value:
                code: Unauthorized
                title: Unauthorized
                status: 401
                instance: /
                detail: Токен истек или недействителен
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
    Forbidden:
      description: >
        Недостаточно прав для выполнения операции. Возвращается в следующих
        случаях:


        - **Отсутствует необходимый scope в токене**: 
          Токен валиден, но не содержит требуемый scope для данной операции.
          Например, для операции бронирования требуется scope `booking.lounges:write`, 
          а в токене присутствует только `catalog.lounges:read`.

        - **Неверный audience (aud) в токене**: 
          Поле `aud` в JWT не соответствует ожидаемому значению для данного API.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing_scope:
              summary: Отсутствует необходимый scope
              value:
                code: Forbidden
                title: Forbidden
                status: 403
                instance: /v2/b2b/booking/lounges
                detail: >-
                  Недостаточно прав для операции (требуется scope:
                  booking.lounges:write)
                details:
                  required_scope: booking.lounges:write
                  provided_scopes:
                    - catalog.lounges:read
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
            invalid_audience:
              summary: Неверный audience в токене
              value:
                code: Forbidden
                title: Forbidden
                status: 403
                instance: /v2/b2b/booking/lounges
                detail: Токен выдан для другого API (неверный audience)
                details:
                  expected_aud: https://api.every.ru/business-lounges
                  provided_aud: https://api.every.ru/other-service
                request_id: f2c8a9f6-1234-5678-9abc-def012345678
    IdempotencyConflict:
      description: >
        Конфликт идемпотентности. Возвращается, когда запрос с существующим
        `Idempotency-Key` 

        содержит тело запроса, отличающееся от сохранённого при первом
        использовании ключа.


        **Правила сравнения**:

        - Для POST /v2/b2b/booking/lounges: сравниваются все поля
        `LoungeBookingRequest`

        - Для POST /v2/b2b/booking/fast-tracks: сравниваются все поля
        `FastTrackBookingRequest`

        - Для POST /v2/b2b/fulfilment/esims: сравниваются все поля
        `CreateAccountEsimRequest` (package_template_id, active_period,
        validity_period)

        - Для POST /v2/b2b/fulfilment/esims/{iccid}/topup: сравниваются путь
        (`iccid`) и все поля `CreateAccountEsimRequest` (package_template_id,
        active_period, validity_period)

        - Сравнение выполняется путём нормализации JSON и побайтового сравнения


        **Важно**: Если тело запроса совпадает, возвращается исходный ответ с
        `Idempotency-Status: reused`, 

        а не эта ошибка.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: IdempotencyConflict
            title: Idempotency conflict
            status: 409
            instance: /v2/b2b/booking/lounges
            detail: Idempotency Key используется с другими параметрами
            request_id: xxxx
    InternalError:
      description: Внутренняя ошибка сервиса
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: InternalError
            title: Внутренняя ошибка сервиса
            status: 500
            instance: /
            detail: Внутренняя ошибка сервиса
            request_id: xxxx
    ServiceUnavailable:
      description: >
        Сервис временно недоступен. 

        Клиент может повторить запрос позже. Рекомендуется использовать
        экспоненциальную задержку между повторными попытками.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: ServiceUnavailable
            title: Сервис временно недоступен
            status: 503
            instance: /
            detail: Сервис временно недоступен
            request_id: xxxx

````