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

# Создание eSIM

> Берёт свободную eSIM из пула клиента и назначает начальный пакет
по `package_template_id` одной атомарной операцией. Возвращает данные
активации выпущенной eSIM.

Параметры `active_period` и `validity_period` опциональны и переопределяют
стандартный срок действия пакета из шаблона.

Этот вызов расходует eSIM из пула, поэтому всегда передавайте ключ идемпотентности,
чтобы исключить повторный выпуск на одну операцию при повторном вызове.


Берёт свободную eSIM из пула партнёра и назначает ей начальный пакет трафика
по `package_template_id` одной атомарной операцией. В ответ возвращаются данные
для активации выпущенной eSIM: `iccid`, код активации, QR-код в формате LPA и
адрес SM-DP+ сервера.

Параметры `active_period` и `validity_period` опциональны и переопределяют
стандартный срок действия пакета из шаблона.

<Note>
  Вызов расходует eSIM из пула. Всегда передавайте заголовок `Idempotency-Key`,
  чтобы исключить повторный выпуск на одну операцию при повторной отправке запроса.
  Остаток пула можно проверить через [Остаток eSIM в пуле](/api-reference/v2/esim/inventory).
</Note>


## OpenAPI

````yaml POST /v2/b2b/fulfilment/esims
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/fulfilment/esims:
    post:
      tags:
        - b2b
        - esim
        - fulfilment
      summary: Создание eSIM
      description: >
        Берёт свободную eSIM из пула клиента и назначает начальный пакет

        по `package_template_id` одной атомарной операцией. Возвращает данные

        активации выпущенной eSIM.


        Параметры `active_period` и `validity_period` опциональны и
        переопределяют

        стандартный срок действия пакета из шаблона.


        Этот вызов расходует eSIM из пула, поэтому всегда передавайте ключ
        идемпотентности,

        чтобы исключить повторный выпуск на одну операцию при повторном вызове.
      operationId: accountCreateEsim
      parameters:
        - $ref: '#/components/parameters/RequestID'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccountEsimRequest'
      responses:
        '201':
          description: eSIM создана, возвращены данные для активации
          headers:
            Idempotency-Key:
              $ref: '#/components/headers/IdempotencyKey'
            Idempotency-Status:
              $ref: '#/components/headers/IdempotencyStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAccountEsimResponse'
              example:
                iccid: '8948010000000000000'
                activation_code: K1-1JL898-DKUTDC
                qr: LPA:1$smdp.io$K1-1JL898-DKUTDC
                smdp: smdp.io
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '422':
          $ref: '#/components/responses/EsimPoolExhausted'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - Bearer:
            - esim: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:
    CreateAccountEsimRequest:
      type: object
      description: >
        Обратите внимание, что active_period и validity_period 

        переопределяют стандартную механику срока действия пакета из шаблона.


        При указании validity_period пакет начинает быть активным с момента его
        привязывание к симкарте.

        При указании active_period указывается окно его действия с определенной
        даты по определенную.
      required:
        - package_template_id
      properties:
        package_template_id:
          type: integer
          description: Шаблон пакета
          example: 132
        active_period:
          $ref: '#/components/schemas/CreateAccountEsimRequestActivePeriod'
        validity_period:
          type: integer
          description: Срок действия пакета в днях от момента вызова.
          example: 30
    CreateAccountEsimResponse:
      type: object
      description: |
        Данные активации выпущенной eSIM. Возвращается в ответ на успешное
        создание eSIM через POST /v2/b2b/fulfilment/esims.
      required:
        - iccid
        - activation_code
        - qr
        - smdp
      properties:
        iccid:
          $ref: '#/components/schemas/Iccid'
        activation_code:
          type: string
          description: Уникальный идентификатор профиля на SM-DP+ сервере
          example: K1-1JL898-DKUTDC
        qr:
          $ref: '#/components/schemas/LpaActivationCode'
        smdp:
          $ref: '#/components/schemas/Smdp'
    IdempotencyKey:
      type: string
      maxLength: 256
      description: >
        Уникальный ключ идемпотентности запроса. Может быть любой строкой длиной
        до 256 символов.

        Рекомендуется использовать UUID для обеспечения глобальной уникальности.
      example: 550e8400-e29b-41d4-a716-446655440000
    CreateAccountEsimRequestActivePeriod:
      type: object
      description: Точный период действия.
      required:
        - start
        - end
      properties:
        start:
          type: string
          format: date-time
          description: Начало периода действия
          example: '2025-11-08T06:00:00.000Z'
        end:
          type: string
          format: date-time
          description: Конец периода действия
    IdempotencyStatus:
      type: string
      enum:
        - created
        - reused
    Iccid:
      type: string
      description: ICCID — серийный номер eSIM-профиля (19–20 цифр)
      example: '8948010000000000000'
      pattern: ^[0-9]+$
      minLength: 19
      maxLength: 20
    LpaActivationCode:
      type: string
      example: LPA:1$smdp.io$K1-1JL898-DKUTDC
      description: >
        Строка активации eSIM в нотации LPA:
        `LPA:version$smdp_address$activation_code`.


        - `LPA:` — префикс, указывающий, что строка предназначена для Local
        Profile Assistant (компонент eSIM на устройстве).

        - `version` — версия формата кода активации.

        - `smdp_address` — адрес SM-DP+ сервера (Subscription Manager Data
        Preparation).

        - `activation_code` — уникальный идентификатор конкретного профиля на
        SM-DP+ сервере.
    Smdp:
      type: string
      description: Адрес SM-DP+ сервера, на котором размещён профиль eSIM
      example: smdp.io
      format: hostname
    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
    EsimPoolExhausted:
      description: |
        В пуле клиента нет свободной eSIM для назначения пакета.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: EsimPoolExhausted
            title: Нет свободных eSIM в пуле
            status: 422
            instance: /v2/b2b/fulfilment/esims
            detail: В пуле клиента нет свободной eSIM
            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

````