﻿---
openapi: 3.1.0
info:
  title: Kwiga Public API
  version: 1.0.0
  description: Auto-generated from Slate request/schema YAMLs. Slate docs at https://api-doc.kwiga.com/
    remain the human-facing source of truth; this file is a downstream artifact for
    Postman, SDK codegen and OpenAPI tooling. Multi-language field descriptions are
    preserved under the `x-translations` extension.
servers:
- url: https://api.kwiga.com/api/v1
security:
- TokenAuth: []
  CabinetHash: []
tags:
- name: certificates
- name: contacts
- name: coupons
- name: courses
- name: mailing
- name: offers
- name: products
- name: timezones
paths:
  "/certificates/by-number/{number}":
    get:
      operationId: certificates_show_by_number
      tags:
      - certificates
      summary: Get certificate by number
      parameters:
      - name: number
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Certificate"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts":
    get:
      operationId: contacts_index
      tags:
      - contacts
      summary: Get contacts list
      parameters:
      - name: filters[is_active]
        in: query
        required: false
        schema:
          type: integer
          example: 1
        description: Filter by Active Contacts
        x-translations:
          ru: Фильтр по активных контактам
          uk: Фільтр активних контактів
      - name: filters[date_from]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2024-01-01'
        description: Filter by creation date. Parameter 'from'
        x-translations:
          ru: Фильтр по дате создания. Параметр 'от'
          uk: Фільтр за датою створення. Параметр 'від'
      - name: filters[date_to]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2024-12-31'
        description: Filter by creation date. 'To' parameter
        x-translations:
          ru: Фильтр по дате создания. Параметр 'до'
          uk: Фільтр за датою створення. Параметр 'до'
      - name: filters[last_activity_from]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2024-01-01'
        description: Filter by last activity date. Parameter 'from'
        x-translations:
          ru: Фильтр по дате последней активность. Параметр 'от'
          uk: Фільтр за датою останньої активності. Параметр 'від'
      - name: filters[last_activity_to]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2024-12-31'
        description: Filter by last activity date. 'To' parameter
        x-translations:
          ru: Фильтр по дате последней активность. Параметр 'до'
          uk: Фільтр за датою останньої активності. Параметр 'до'
      - name: filters[search]
        in: query
        required: false
        schema:
          type: string
          example: example.com
        description: Filter by email, phone, name
        x-translations:
          ru: Фильтр по email, телефону, ФИО
          uk: Фільтр по email, телефону, ПІБ
      - name: filters[utm_source]
        in: query
        required: false
        schema:
          type: string
          example: google
        description: utm_source
      - name: filters[utm_campaign]
        in: query
        required: false
        schema:
          type: string
          example: summer_sale
        description: utm_campaign
      - name: filters[utm_medium]
        in: query
        required: false
        schema:
          type: string
          example: email
        description: utm_medium
      - name: filters[utm_term]
        in: query
        required: false
        schema:
          type: string
          example: online courses
        description: utm_term
      - name: filters[utm_content]
        in: query
        required: false
        schema:
          type: string
          example: banner_top
        description: utm_content
      - name: filters[offers]
        in: query
        required: false
        schema:
          type: array
          items:
            type: integer
          example:
          - 101
          - 102
        description: offers ids
      - name: filters[products]
        in: query
        required: false
        schema:
          type: array
          items:
            type: integer
          example:
          - 130
          - 142
        description: products ids
      - name: filters[emails]
        in: query
        required: false
        schema:
          oneOf:
          - type: string
          - type: array
            items:
              type: string
        description: Filter by exact email address. Accepts a comma-separated string
          or a repeated array; each value must be a valid email.
        x-translations:
          ru: Фильтр по точному email-адресу. Принимает строку через запятую или повторяющийся
            массив; каждое значение должно быть валидным email.
          uk: Фільтр за точним email-адресою. Приймає рядок через кому або повторюваний
            масив; кожне значення має бути валідним email.
      - name: filters[contact_ids]
        in: query
        required: false
        schema:
          oneOf:
          - type: integer
          - type: array
            items:
              type: integer
        description: Filter by contact ids. Accepts a comma-separated string or a
          repeated array of integers.
        x-translations:
          ru: Фильтр по id контактов. Принимает строку через запятую или повторяющийся
            массив целых чисел.
          uk: Фільтр за id контактів. Приймає рядок через кому або повторюваний масив
            цілих чисел.
      - name: filters[user_ids]
        in: query
        required: false
        schema:
          oneOf:
          - type: integer
          - type: array
            items:
              type: integer
        description: Filter by user (student account) ids. Accepts a comma-separated
          string or a repeated array of integers.
        x-translations:
          ru: Фильтр по id юзеров (аккаунтов ученика). Принимает строку через запятую
            или повторяющийся массив целых чисел.
          uk: Фільтр за id юзерів (акаунтів учня). Приймає рядок через кому або повторюваний
            масив цілих чисел.
      - name: with_orders
        in: query
        required: false
        schema:
          type: boolean
          example: true
        description: Additionally get information on contact orders and offers
        x-translations:
          ru: Дополнительно получить информацию по заказам и предложениям контакта
          uk: Додатково отримати інформацію по замовленням та пропозиціям контакта
      - name: with_certificates
        in: query
        required: false
        schema:
          type: boolean
          example: true
        description: Get additional information on contact certificates
        x-translations:
          ru: Дополнительно получить информацию по сертификатам контакта
          uk: Додатково отримати інформацію по сертифікатам контакта
      - name: page
        in: query
        required: false
        schema:
          type: integer
          default: 1
          example: 1
        description: Page Number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          default: 15
          example: 15
        description: Number of fetch items
        x-translations:
          ru: Кол-во элементов выборки
          uk: Кількість елементів вибірки
      - name: sort_by
        in: query
        required: false
        schema:
          type: string
          default: desc
          enum:
          - asc
          - desc
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Contact"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
    post:
      operationId: contacts_store
      tags:
      - contacts
      summary: Create contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: Email - should be unique value within an account
                  x-translations:
                    ru: Email - должен быть уникальным в рамках кабинета
                    uk: Email - має бути унікальним у рамках кабінету
                  example: user@example.com
                phone:
                  type: string
                  description: 'Phone number — format: +{country code}{phone}'
                  x-translations:
                    ru: 'Номер телефона — формат: +{код страны}{телефон}'
                    uk: 'Номер телефону — формат: +{код країни}{телефон}'
                  example: "+380931234567"
                first_name:
                  type: string
                  description: Contact first name
                  x-translations:
                    ru: Имя контакта
                    uk: Ім'я контакту
                  example: John
                middle_name:
                  type: string
                  description: Contact middle name (or patronymic)
                  x-translations:
                    ru: Отчество контакта (или second/middle name)
                    uk: По-батькові контакту (або middle name)
                  example: Michael
                last_name:
                  type: string
                  description: Contact last name
                  x-translations:
                    ru: Фамилия контакта
                    uk: Прізвище контакту
                  example: Doe
                name:
                  type: string
                  description: Full name as a single string. Use this when you do
                    not have first/middle/last split available; the server will keep
                    it as the contact's name.
                  x-translations:
                    ru: Полное имя одной строкой. Используйте, если у вас нет разбивки
                      на имя/отчество/фамилию — сервер сохранит значение как имя контакта.
                    uk: Повне ім'я одним рядком. Використовуйте, якщо у вас немає
                      розбивки на ім'я/по-батькові/прізвище — сервер збереже значення
                      як ім'я контакту.
                  example: John Michael Doe
                tags:
                  type: array
                  items:
                    type: string
                  description: Tags
                  x-translations:
                    ru: Теги
                    uk: Теги
                  example:
                  - vip
                  - webinar
                send_activation_email:
                  type: boolean
                  description: Send a welcome email to the contact.
                  x-translations:
                    ru: Отправить приветственное письмо
                    uk: Надіслати вітальний лист
                  default: false
                locale:
                  type: string
                  description: Contact's language in iso_2 format. See <a href="#supported-locales">the
                    supported locales list</a> for all values.
                  x-translations:
                    ru: Язык контакта в формате iso_2. Полный список значений см.
                      в <a href="#supported-locales">блоке поддерживаемых локалей</a>.
                    uk: Мова контакту в форматі iso_2. Повний список значень див.
                      у <a href="#supported-locales">блоці локалей, які підтримуються</a>.
                  default: en
                create_order:
                  type: boolean
                  description: Create an empty order
                  x-translations:
                    ru: Создать пустой заказ
                    uk: Створити пусте замовлення
                  example: false
                order_stage_id:
                  type: integer
                  description: Identifier of order stage in funnel (can be obtained
                    in <em>CRM &rarr; Orders &rarr; Settings &rarr; Status list</em>)
                  x-translations:
                    ru: Идентификатор статуса заказа в воронке (можно получить на
                      странице <em>CRM &rarr; Заказы &rarr; Настройки &rarr; Список
                      статусов</em>)
                    uk: Ідентифікатор статусу замовлення у воронці (можна отримати
                      на сторінці <em>CRM &rarr; Замовлення &rarr; Налаштування &rarr;
                      Список статусів</em>)
                  example: 1
                additional_fields:
                  type: array
                  items:
                    type: object
                    properties:
                      field_id:
                        type: integer
                        description: Custom field id (can be obtained in <em>CRM &rarr;
                          Contacts &rarr; Settings &rarr; Add custom fields</em>)
                        x-translations:
                          ru: id пользовательского поля (можно получить в <em>CRM
                            &rarr; Контакты &rarr; Настройки &rarr; Добавление пользовательских
                            полей</em>)
                          uk: id кастомного поля (можна отримати в <em>CRM &rarr;
                            Контакти &rarr; Налаштування &rarr; Додавання полів користувача</em>)
                        example: 17
                      value:
                        type: string
                        description: Custom field value
                        x-translations:
                          ru: Значение пользовательского поля
                          uk: Значення кастомного поля
                        example: Custom field value
                  description: Custom fields<br/>
                  x-translations:
                    ru: Пользовательские (кастомные) поля<br/>
                    uk: Поля користувача (кастомні поля) <br/>
              required:
              - email
            examples:
              basic:
                summary: Create new contact
                value:
                  first_name: James
                  last_name: Bond
                  email: bond@example.com
                  send_activation_email: true
                  phone: "+380931234567"
                  manager_ids:
                  - 264
                  - 288
                  additional_fields:
                  - field_id: 17
                    value: this is test value
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Contact"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/products":
    delete:
      operationId: contacts_products_delete_bulk
      tags:
      - contacts
      summary: Delete contact products in bulk
      parameters:
      - name: email
        in: query
        required: false
        schema:
          type: string
          example: user@example.com
        description: Contact email. Required if contact_id is not provided
        x-translations:
          ru: Email контакта. Обязательный если contact_id не представлен
          uk: Email контакту. Обов'язковий якщо contact_id не вказаний
      - name: contact_id
        in: query
        required: false
        schema:
          type: integer
          example: 132
        description: Contact id. Required if email is not provided
        x-translations:
          ru: Id контакта. Обязательный если email не представлен
          uk: Id контакту. Обов'язковий якщо email не вказаний
      - name: product_id
        in: query
        required: false
        schema:
          type: integer
          example: 130
        description: Product id. Required if offers is not provided. Can be obtained
          from the Contact Products List endpoint or from the Courses List (it will
          be the product_id field in the course)
        x-translations:
          ru: Id продукта. Обязательный если offers не представлен. Можно получить
            например в эндпоинте Список продуктов контакта или в Списке курсов (это
            будет product_id поле в курсе)
          uk: Id продукту. Обов'язковий якщо offers не вказаний. Можна отримати наприклад
            в ендпоінті Список продуктів контакту або в Списку курсів (це буде поле
            product_id в курсі)
      - name: offers
        in: query
        required: false
        schema:
          type: array
          items:
            type: object
          example:
          - 101
          - 102
        description: Array of offer ids by which subscriptions are deleted. Required
          if product_id is not provided. Can be obtained from the offer edit page
          URL or from the API in the contact offers list.
        x-translations:
          ru: Массив с id предложений по которым удаляем подписки. Обязательный если
            product_id не представлен. Можно получить в адресной строке редактирования
            предложения или в апи в списке предложений контакта.
          uk: Масив з id пропозицій по яких видаляємо підписки. Обов'язковий якщо
            product_id не вказаний. Можна отримати в адресному рядку редагування пропозиції
            або в апі в списку пропозицій контакту.
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/AffectedSubscriptions"
                  contact_id:
                    type: integer
                    description: ID of the contact whose subscriptions were affected
                required:
                - data
                - contact_id
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/purchases":
    post:
      operationId: contacts_purchases
      tags:
      - contacts
      summary: Add purchase to contact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: Email - should be unique value within an account
                  x-translations:
                    ru: Email - должен быть уникальным в рамках кабинета
                    uk: Email - має бути унікальним у рамках кабінету
                  example: user@example.com
                phone:
                  type: string
                  description: 'Phone number — format: +{country code}{phone}'
                  x-translations:
                    ru: 'Номер телефона — формат: +{код страны}{телефон}'
                    uk: 'Номер телефону — формат: +{код країни}{телефон}'
                  example: "+380931234567"
                first_name:
                  type: string
                  description: Contact first name
                  x-translations:
                    ru: Имя контакта
                    uk: Ім'я контакту
                  example: John
                middle_name:
                  type: string
                  description: Contact middle name (or patronymic)
                  x-translations:
                    ru: Отчество контакта (или second/middle name)
                    uk: По-батькові контакту (або middle name)
                  example: Michael
                last_name:
                  type: string
                  description: Contact last name
                  x-translations:
                    ru: Фамилия контакта
                    uk: Прізвище контакту
                  example: Doe
                name:
                  type: string
                  description: Full name as a single string. Use this when you do
                    not have first/middle/last split available; the server will keep
                    it as the contact's name.
                  x-translations:
                    ru: Полное имя одной строкой. Используйте, если у вас нет разбивки
                      на имя/отчество/фамилию — сервер сохранит значение как имя контакта.
                    uk: Повне ім'я одним рядком. Використовуйте, якщо у вас немає
                      розбивки на ім'я/по-батькові/прізвище — сервер збереже значення
                      як ім'я контакту.
                  example: John Michael Doe
                tags:
                  type: array
                  items:
                    type: string
                  description: Tags
                  x-translations:
                    ru: Теги
                    uk: Теги
                send_activation_email:
                  type: boolean
                  description: Send a welcome email to the contact.
                  x-translations:
                    ru: Отправить приветственное письмо
                    uk: Надіслати вітальний лист
                  default: false
                send_product_access_email:
                  type: boolean
                  description: Send an email telling the contact that product access
                    has been granted.
                  x-translations:
                    ru: Отправить письмо о доступе к продукту
                    uk: Надіслати лист про доступ до продукту
                  default: false
                send_payment_success_email:
                  type: boolean
                  description: Send an email confirming a successful offer payment.
                  x-translations:
                    ru: Отправить письмо об успешной оплате предложения
                    uk: Надіслати лист про успішну оплату пропозиції
                  default: false
                locale:
                  type: string
                  description: Contact's language in iso_2 format. See <a href="#supported-locales">the
                    supported locales list</a> for all values.
                  x-translations:
                    ru: Язык контакта в формате iso_2. Полный список значений см.
                      в <a href="#supported-locales">блоке поддерживаемых локалей</a>.
                    uk: Мова контакту в форматі iso_2. Повний список значень див.
                      у <a href="#supported-locales">блоці локалей, які підтримуються</a>.
                  default: en
                offer_id:
                  type: integer
                  description: Offer identifier — taken from the end of the offer
                    edit page URL (e.g. <code>https://sample-school.kwiga.com/expert/payments/offers/edit/3858</code>).
                    If not provided, the request falls back to the <code>product_ids</code>
                    field.
                  x-translations:
                    ru: Идентификатор предложения — берётся из конца ссылки страницы
                      редактирования предложения (например, <code>https://sample-school.kwiga.com/expert/payments/offers/edit/3858</code>).
                      Если не передан — запрос обращается к полю <code>product_ids</code>.
                    uk: Ідентифікатор пропозиції — береться з кінця посилання сторінки
                      редагування пропозиції (наприклад, <code>https://sample-school.kwiga.com/expert/payments/offers/edit/3858</code>).
                      Якщо не переданий — запит звертається до поля <code>product_ids</code>.
                  example: 3858
                product_ids:
                  type: array
                  items:
                    type: integer
                  description: Array of product IDs.<br/> If offer_id and product_ids
                    are missing, an empty order will be created
                  x-translations:
                    ru: Массив id продуктов.<br/> Если offer_id и product_ids отсутсвуют,
                      то будет создан пустой заказ
                    uk: Масив id продуктів.<br/> Якщо offer_id і product_ids відсутні,
                      то буде створено пусте замовлення
                  example:
                  - 130
                  - 142
                order_stage_id:
                  type: integer
                  description: Identifier of order stage in funnel (can be obtained
                    in <em>CRM &rarr; Orders &rarr; Settings &rarr; Status list</em>)
                  x-translations:
                    ru: Идентификатор статуса заказа в воронке (можно получить на
                      странице <em>CRM &rarr; Заказы &rarr; Настройки &rarr; Список
                      статусов</em>)
                    uk: Ідентифікатор статусу замовлення у воронці (можна отримати
                      на сторінці <em>CRM &rarr; Замовлення &rarr; Налаштування &rarr;
                      Список статусів</em>)
                  example: 1
                is_paid:
                  type: boolean
                  description: Marks the created order as paid.
                  x-translations:
                    ru: Помечает созданный заказ как оплаченный.
                    uk: Позначає створене замовлення як сплачене.
                  default: true
                manager_ids:
                  type: array
                  items:
                    type: integer
                  description: Managers to order (can be obtained in *Settings->Administration
                    access*)
                  x-translations:
                    ru: Менеджеры заказа (можно получить на странице *Настройки->Доступи
                      по управлению*)
                    uk: Менеджери замовлення (можна отримати на сторінці <em>Налаштування
                      &rarr; Доступи по управлінню</em>)
                  example:
                  - 5
                  - 12
                comment:
                  type: string
                  description: 'Order comment. Max: 5000'
                  x-translations:
                    ru: 'Комментарий к заказу. Max: 5000'
                    uk: 'Коментар до замовлення. Max: 5000'
                  maxLength: 5000
                  example: Order from webinar
                additional_fields:
                  type: array
                  items:
                    type: object
                    properties:
                      field_id:
                        type: integer
                        description: Custom field id (can be obtained in <em>CRM &rarr;
                          Contacts &rarr; Settings &rarr; Add custom fields</em>)
                        x-translations:
                          ru: id пользовательского поля (можно получить в <em>CRM
                            &rarr; Контакты &rarr; Настройки &rarr; Добавление пользовательских
                            полей</em>)
                          uk: id кастомного поля (можна отримати в <em>CRM &rarr;
                            Контакти &rarr; Налаштування &rarr; Додавання полів користувача</em>)
                      value:
                        type: string
                        description: Custom field value
                        x-translations:
                          ru: Значение пользовательского поля
                          uk: Значення кастомного поля
                  description: Custom fields<br/>
                  x-translations:
                    ru: Пользовательские (кастомные) поля<br/>
                    uk: Поля користувача (кастомні поля) <br/>
              required:
              - email
            examples:
              basic:
                summary: Add purchase to contact
                value:
                  first_name: James
                  last_name: Bond
                  email: bond@example.com
                  send_activation_email: true
                  phone: "+380931234567"
                  offer_id: 18
                  additional_fields:
                  - field_id: 17
                    value: this is test value
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Contact"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/query":
    post:
      operationId: contacts_query
      tags:
      - contacts
      summary: Query contacts (POST alias for GET /contacts)
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                filters:
                  type: object
                  description: Filter parameters
                  properties:
                    is_active:
                      type: integer
                      description: Filter by Active Contacts
                      x-translations:
                        ru: Фильтр по активных контактам
                        uk: Фільтр активних контактів
                      example: 1
                    date_from:
                      type: string
                      format: date-time
                      description: Filter by creation date. Parameter 'from'
                      x-translations:
                        ru: Фильтр по дате создания. Параметр 'от'
                        uk: Фільтр за датою створення. Параметр 'від'
                      example: '2024-01-01'
                    date_to:
                      type: string
                      format: date-time
                      description: Filter by creation date. 'To' parameter
                      x-translations:
                        ru: Фильтр по дате создания. Параметр 'до'
                        uk: Фільтр за датою створення. Параметр 'до'
                      example: '2024-12-31'
                    last_activity_from:
                      type: string
                      format: date-time
                      description: Filter by last activity date. Parameter 'from'
                      x-translations:
                        ru: Фильтр по дате последней активность. Параметр 'от'
                        uk: Фільтр за датою останньої активності. Параметр 'від'
                      example: '2024-01-01'
                    last_activity_to:
                      type: string
                      format: date-time
                      description: Filter by last activity date. 'To' parameter
                      x-translations:
                        ru: Фильтр по дате последней активность. Параметр 'до'
                        uk: Фільтр за датою останньої активності. Параметр 'до'
                      example: '2024-12-31'
                    search:
                      type: string
                      description: Filter by email, phone, name
                      x-translations:
                        ru: Фильтр по email, телефону, ФИО
                        uk: Фільтр по email, телефону, ПІБ
                      example: example.com
                    utm_source:
                      type: string
                      description: utm_source
                      example: google
                    utm_campaign:
                      type: string
                      description: utm_campaign
                      example: summer_sale
                    utm_medium:
                      type: string
                      description: utm_medium
                      example: email
                    utm_term:
                      type: string
                      description: utm_term
                      example: online courses
                    utm_content:
                      type: string
                      description: utm_content
                      example: banner_top
                    offers:
                      type: array
                      items:
                        type: integer
                      description: offers ids
                      example:
                      - 101
                      - 102
                    products:
                      type: array
                      items:
                        type: integer
                      description: products ids
                      example:
                      - 130
                      - 142
                    emails:
                      oneOf:
                      - type: string
                      - type: array
                        items:
                          type: string
                      description: Filter by exact email address. Accepts a comma-separated
                        string or a repeated array; each value must be a valid email.
                      x-translations:
                        ru: Фильтр по точному email-адресу. Принимает строку через
                          запятую или повторяющийся массив; каждое значение должно
                          быть валидным email.
                        uk: Фільтр за точним email-адресою. Приймає рядок через кому
                          або повторюваний масив; кожне значення має бути валідним
                          email.
                    contact_ids:
                      oneOf:
                      - type: integer
                      - type: array
                        items:
                          type: integer
                      description: Filter by contact ids. Accepts a comma-separated
                        string or a repeated array of integers.
                      x-translations:
                        ru: Фильтр по id контактов. Принимает строку через запятую
                          или повторяющийся массив целых чисел.
                        uk: Фільтр за id контактів. Приймає рядок через кому або повторюваний
                          масив цілих чисел.
                    user_ids:
                      oneOf:
                      - type: integer
                      - type: array
                        items:
                          type: integer
                      description: Filter by user (student account) ids. Accepts a
                        comma-separated string or a repeated array of integers.
                      x-translations:
                        ru: Фильтр по id юзеров (аккаунтов ученика). Принимает строку
                          через запятую или повторяющийся массив целых чисел.
                        uk: Фільтр за id юзерів (акаунтів учня). Приймає рядок через
                          кому або повторюваний масив цілих чисел.
                with_orders:
                  type: boolean
                  description: Additionally get information on contact orders and
                    offers
                  x-translations:
                    ru: Дополнительно получить информацию по заказам и предложениям
                      контакта
                    uk: Додатково отримати інформацію по замовленням та пропозиціям
                      контакта
                  example: true
                with_certificates:
                  type: boolean
                  description: Get additional information on contact certificates
                  x-translations:
                    ru: Дополнительно получить информацию по сертификатам контакта
                    uk: Додатково отримати інформацію по сертифікатам контакта
                  example: true
                page:
                  type: integer
                  description: Page Number
                  x-translations:
                    ru: Номер страницы
                    uk: Номер сторінки
                  default: 1
                  example: 1
                per_page:
                  type: integer
                  description: Number of fetch items
                  x-translations:
                    ru: Кол-во элементов выборки
                    uk: Кількість елементів вибірки
                  default: 15
                  example: 15
                sort_by:
                  type: string
                  default: desc
                  enum:
                  - asc
                  - desc
            examples:
              with_filters:
                summary: Query contacts filtered by emails and tags
                value:
                  filters:
                    emails:
                    - alice@example.com
                    - bob@example.com
                    contact_ids:
                    - 1
                    - 2
                    - 3
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Contact"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/tags":
    post:
      operationId: contacts_tags_add
      tags:
      - contacts
      summary: Add tags to contacts
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Success"
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
    delete:
      operationId: contacts_tags_remove
      tags:
      - contacts
      summary: Remove tags from contacts
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Success"
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/{contact}/products":
    get:
      operationId: contacts_products
      tags:
      - contacts
      summary: Get contact products
      parameters:
      - name: contact
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/UserProduct"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/{contact}/products/{product}":
    delete:
      operationId: contacts_products_delete
      tags:
      - contacts
      summary: Delete contact product
      parameters:
      - name: contact
        in: path
        required: true
        schema:
          type: string
      - name: product
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Success"
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/contacts/{id}":
    get:
      operationId: contacts_show
      tags:
      - contacts
      summary: Get contact by ID
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      - name: with_certificates
        in: query
        required: false
        schema:
          type: boolean
          example: true
        description: Get additional information on contact certificates
        x-translations:
          ru: Дополнительно получить информацию по сертификатам контакта
          uk: Додатково отримати інформацію по сертифікатам контакта
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Contact"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
    put:
      operationId: contacts_update
      tags:
      - contacts
      summary: Update contact
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: Email - should be unique value within an account
                  x-translations:
                    ru: Email - должен быть уникальным в рамках кабинета
                    uk: Email - має бути унікальним у рамках кабінету
                  example: user@example.com
                phone:
                  type: string
                  description: 'Phone number — format: +{country code}{phone}'
                  x-translations:
                    ru: 'Номер телефона — формат: +{код страны}{телефон}'
                    uk: 'Номер телефону — формат: +{код країни}{телефон}'
                  example: "+380931234567"
                first_name:
                  type: string
                  description: Contact first name
                  x-translations:
                    ru: Имя контакта
                    uk: Ім'я контакту
                  example: John
                last_name:
                  type: string
                  description: Contact last name
                  x-translations:
                    ru: Фамилия контакта
                    uk: Прізвище контакту
                  example: Doe
                tags:
                  type: array
                  items:
                    type: string
                  description: Tags. They work in sync mode. That is, the contact
                    will only have those tags that will be transferred
                  x-translations:
                    ru: Теги. Работают в режиме sync. То есть на контакте будут только
                      те теги, которые будут переданы
                    uk: Теги. Працюють в режимі sync. Тобто на контакті будуть тільки
                      ті теги, які будуть передані
                  example:
                  - vip
                  - webinar
                additional_fields:
                  type: array
                  items:
                    type: object
                    properties:
                      field_id:
                        type: integer
                        description: Custom field id (can be obtained in <em>CRM &rarr;
                          Contacts &rarr; Settings &rarr; Add custom fields</em>)
                        x-translations:
                          ru: id пользовательского поля (можно получить в <em>CRM
                            &rarr; Контакты &rarr; Настройки &rarr; Добавление пользовательских
                            полей</em>)
                          uk: id кастомного поля (можна отримати в <em>CRM &rarr;
                            Контакти &rarr; Налаштування &rarr; Додавання полів користувача</em>)
                        example: 17
                      value:
                        type: string
                        description: Custom field value
                        x-translations:
                          ru: Значение пользовательского поля
                          uk: Значення кастомного поля
                        example: Updated value
                  description: Custom fields<br/>
                  x-translations:
                    ru: Пользовательские (кастомные) поля<br/>
                    uk: Поля користувача (кастомні поля) <br/>
            examples:
              basic:
                summary: Update contact
                value:
                  first_name: John
                  last_name: Black
                  email: bond123@example.com
                  phone: "+380931112233"
                  tags:
                  - test-tag
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Contact"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/coupons":
    get:
      operationId: coupons_index
      tags:
      - coupons
      summary: Get coupons list
      parameters:
      - name: filters[date_from]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2022-04-15'
        description: Filter by creation date. Parameter 'from'
        x-translations:
          ru: Фильтр по дате создания. Параметр 'от'
          uk: Фільтр за датою створення. Параметр 'від'
      - name: filters[date_to]
        in: query
        required: false
        schema:
          type: string
          format: date-time
          example: '2022-04-27'
        description: Filter by creation date. 'To' parameter
        x-translations:
          ru: Фильтр по дате создания. Параметр 'до'
          uk: Фільтр за датою створення. Параметр 'до'
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ExpertCoupon"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
    post:
      operationId: coupons_store
      tags:
      - coupons
      summary: Create coupon
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: Coupon code. If you do not specify, the code is generated
                    automatically
                  x-translations:
                    ru: Код купона. Если не указать, то код сгенерируется автоматически
                    uk: Код купону. Якщо не вказати, код згенерується автоматично
                  example: SUMMER2024
                type_discount.id:
                  type: string
                  description: 'Discount type: percentage of the amount - `discount_with_percent`
                    or fixed discount - `discount_with_currency`'
                  x-translations:
                    ru: 'Тип скидки: процент от суммы - `discount_with_percent` или
                      фиксированная скидка - `discount_with_currency`'
                    uk: 'Тип знижки: відсоток від суми - `discount_with_percent` або
                      фіксована знижка - `discount_with_currency`'
                  example: discount_with_percent
                expires_at:
                  type: string
                  format: date-time
                  description: Coupon validity period. If null or not specified, then
                    indefinitely
                  x-translations:
                    ru: Срок действия купона. Если null или не указывать, то бессрочно
                    uk: Термін дії купона. Якщо null чи не вказувати, то безстроково
                  nullable: true
                  example: '2024-12-31'
                timezone.id:
                  type: integer
                  description: Time zone of coupon expiration date
                  x-translations:
                    ru: Таймзона даты срока действия купона
                    uk: Таймзона дати терміну дії купона
                  nullable: true
                  example: 375
                total_uses:
                  type: integer
                  description: Limit on the number of times a coupon can be used.
                    0 - unlimited
                  x-translations:
                    ru: Лимит на количество использований купона. 0 - без ограничений
                    uk: Ліміт на кількість використань купона. 0 - без обмежень
                  example: 100
                total_uses_per_user:
                  type: integer
                  description: Limit on the number of times a coupon can be used by
                    one user
                  x-translations:
                    ru: Лимит на количество использований купона одним пользователем
                    uk: Ліміт на кількість використань купона одним користувачем
                  example: 1
              required:
              - type_discount.id
            examples:
              basic:
                summary: Create coupon
                value:
                  code: MY-COUPON
                  reward: 0
                  type_discount[id]: discount_with_percent
                  expires_at: 
                  timezone[id]: 375
                  total_uses: 0
                  total_uses_per_user: 0
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/ExpertCoupon"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/coupons/check":
    post:
      operationId: coupons_check
      tags:
      - coupons
      summary: Check coupon validity
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: Coupon code to check
                  x-translations:
                    ru: Код купона для проверки
                    uk: Код купона для перевірки
                  example: SUMMER2024
                price:
                  type: number
                  description: Price for discount calculation. If specified, the discount
                    value will be returned in the discount field in the response
                  x-translations:
                    ru: Цена для расчёта скидки. Если указать, то в ответе будет возвращено
                      значение скидки в поле discount
                    uk: Ціна для розрахунку знижки. Якщо вказати, то у відповіді буде
                      повернуто значення знижки у полі discount
                  example: 199.99
              required:
              - code
            examples:
              basic:
                summary: Check coupon validity
                value:
                  code: VAJ6-EA
                  price: 65
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/CouponCheck"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/coupons/{id}":
    get:
      operationId: coupons_show
      tags:
      - coupons
      summary: Get coupon by ID
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/ExpertCoupon"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/courses":
    get:
      operationId: courses_index
      tags:
      - courses
      summary: Get courses list
      parameters:
      - name: with
        in: query
        required: false
        schema:
          type: array
          items:
            type: object
          enum:
          - offers
          - description
          - program
          example:
          - offers
          - program
        description: 'Array of additional parameters to get along with the course.
          Possible options: offers (list of all offers with the course), description
          (info blocks with course description), program (course program tree)'
        x-translations:
          ru: 'Массив с дополнительными параметрами которые надо получить вместе с
            курсом. Возможные варианты: offers (список всех офферов с курсом), description
            (инфоблоки с описанием курса), program (дерево программы курса)'
          uk: 'Масив з додатковими параметрами, які потрібно отримати разом з курсом.
            Можливі варіанти: offers (список всіх офферів з курсом), description (інфоблоки
            з описом курсу), program (дерево програми курсу)'
      - name: sort_by
        in: query
        required: false
        schema:
          type: string
          default: desc
          enum:
          - asc
          - desc
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          default: 15
          maximum: 15
          example: 15
        description: Number of items per page
        x-translations:
          ru: Количество элементов на странице
          uk: Кількість елементів на сторінці
      - name: page
        in: query
        required: false
        schema:
          type: integer
          default: 1
          example: 1
        description: Page number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Course"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/courses/{course}/users":
    get:
      operationId: courses_users
      tags:
      - courses
      summary: Get course users
      parameters:
      - name: course
        in: path
        required: true
        schema:
          type: string
      - name: search
        in: query
        required: false
        schema:
          type: string
          example: John
        description: Fuzzy search by email or name
        x-translations:
          ru: Нестрогий поиск по имейлу или имени
          uk: Нестрогий пошук по імейлу або імені
      - name: contact_id
        in: query
        required: false
        schema:
          type: integer
          example: 123
        description: By contact id
        x-translations:
          ru: По id контакта
          uk: По id контакту
      - name: contact_ids
        in: query
        required: false
        schema:
          oneOf:
          - type: integer
          - type: array
            items:
              type: integer
        description: Filter by contact ids. Accepts a comma-separated string or a
          repeated array of integers.
        x-translations:
          ru: Фильтр по id контактов. Принимает строку через запятую или повторяющийся
            массив целых чисел.
          uk: Фільтр за id контактів. Приймає рядок через кому або повторюваний масив
            цілих чисел.
      - name: user_id
        in: query
        required: false
        schema:
          type: integer
          example: 456
        description: By user id
        x-translations:
          ru: По id пользователя
          uk: По id користувача
      - name: user_ids
        in: query
        required: false
        schema:
          oneOf:
          - type: integer
          - type: array
            items:
              type: integer
        description: Filter by user (student account) ids. Accepts a comma-separated
          string or a repeated array of integers.
        x-translations:
          ru: Фильтр по id юзеров (аккаунтов ученика). Принимает строку через запятую
            или повторяющийся массив целых чисел.
          uk: Фільтр за id юзерів (акаунтів учня). Приймає рядок через кому або повторюваний
            масив цілих чисел.
      - name: emails
        in: query
        required: false
        schema:
          oneOf:
          - type: string
          - type: array
            items:
              type: string
        description: Filter by exact email address. Accepts a comma-separated string
          or a repeated array; each value must be a valid email.
        x-translations:
          ru: Фильтр по точному email-адресу. Принимает строку через запятую или повторяющийся
            массив; каждое значение должно быть валидным email.
          uk: Фільтр за точним email-адресою. Приймає рядок через кому або повторюваний
            масив; кожне значення має бути валідним email.
      - name: progress_general_from
        in: query
        required: false
        schema:
          type: integer
          example: 0
        description: General progress from (0 to 99)
        x-translations:
          ru: Общий прогресс от (от 0 до 99)
          uk: Загальний прогрес від (від 0 до 99)
      - name: progress_general_to
        in: query
        required: false
        schema:
          type: integer
          example: 100
        description: General progress to (0 to 100)
        x-translations:
          ru: Общий прогресс до (от 0 до 100)
          uk: Загальний прогрес до (від 0 до 100)
      - name: lessons_viewed_from
        in: query
        required: false
        schema:
          type: integer
          example: 5
        description: Lesson viewing percentage from (0 to 99)
        x-translations:
          ru: Процент просмотра урока от (от 0 до 99)
          uk: Відсоток перегляду уроку від (від 0 до 99)
      - name: lessons_viewed_to
        in: query
        required: false
        schema:
          type: integer
          example: 20
        description: Lesson viewing percentage to (0 to 100)
        x-translations:
          ru: Процент просмотра урока до (от 0 до 100)
          uk: Відсоток перегляду уроку до (від 0 до 100)
      - name: quizzes_progress_from
        in: query
        required: false
        schema:
          type: integer
          example: 0
        description: Quizzes progress percentage from (0 to 99)
        x-translations:
          ru: Процент пройденных практик от (от 0 до 99)
          uk: Відсоток пройдених практик від (від 0 до 99)
      - name: quizzes_progress_to
        in: query
        required: false
        schema:
          type: integer
          example: 100
        description: Quizzes progress percentage to (0 to 100)
        x-translations:
          ru: Процент пройденных практик до (от 0 до 100)
          uk: Відсоток пройдених практик до (від 0 до 100)
      - name: last_activity_from
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Last activity on course from (UTC date)
        x-translations:
          ru: Последняя активность на курсе от (дата UTC)
          uk: Остання активність на курсі від (дата UTC)
      - name: last_activity_to
        in: query
        required: false
        schema:
          type: string
          format: date-time
        description: Last activity on course to (UTC date)
        x-translations:
          ru: Последняя активность на курсе до (дата UTC)
          uk: Остання активність на курсі до (дата UTC)
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          default: 15
          maximum: 250
          example: 15
        description: Number of items per page
        x-translations:
          ru: Количество элементов на странице
          uk: Кількість елементів на сторінці
      - name: page
        in: query
        required: false
        schema:
          type: integer
          default: 1
          example: 1
        description: Page number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      - name: include
        in: query
        required: false
        schema:
          oneOf:
          - type: string
          - type: array
            items:
              type: string
        description: |
          <p>Optional comma-separated list of response extensions. Each token enables one section of the response — clients only pay for what they ask for.</p>
          <ul>
            <li><code>course_program</code> — adds the course program tree at the top level of the response as <code>course_program</code> (array of <a href="#schemas-course-program">CourseProgram</a> nodes; lesson nodes contain <a href="#schemas-course-program-section">CourseProgramSection</a> children with attached quizzes). Returned once next to <code>data</code>/<code>links</code>/<code>meta</code> because it is the same for every user on the page.</li>
            <li><code>lesson_progress</code> — adds <code>lesson_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-lesson-progress">LessonProgress</a> objects (one per lesson the user has progress on).</li>
            <li><code>module_progress</code> — adds <code>module_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-module-progress">ModuleProgress</a> objects.</li>
            <li><code>quiz_progress</code> — adds <code>quiz_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-quiz-progress">QuizProgress</a> objects, one per <code>(course_lesson_id, quiz_id)</code> pair summarised from the user's latest non-cancelled attempt.</li>
          </ul>
          <p>Note: the same quiz can be attached to several lessons of the same course, so <code>quiz_progress</code> is keyed by the lesson/quiz pair, not by <code>quiz_id</code> alone. The client stitches per-user progress to the program tree by <code>course_nodeble_id</code> of lesson/module nodes and by the <code>(lesson.course_nodeble_id, quizzes[i].id)</code> pair for section nodes.</p>
        x-translations:
          ru: |
            <p>Необязательный список расширений ответа через запятую. Каждый токен подключает одну секцию ответа — клиент платит только за то, что попросил.</p>
            <ul>
              <li><code>course_program</code> — добавляет программу курса на верхнем уровне ответа в поле <code>course_program</code> (массив узлов <a href="#schemas-course-program">CourseProgram</a>; у lesson-узлов в <code>children</code> едут <a href="#schemas-course-program-section">CourseProgramSection</a> с привязанными квизами). Возвращается один раз рядом с <code>data</code>/<code>links</code>/<code>meta</code>, потому что программа одинакова для всех учеников страницы.</li>
              <li><code>lesson_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>lesson_progress</code>: массив объектов <a href="#schemas-lesson-progress">LessonProgress</a> (по одной записи на урок, по которому у ученика есть прогресс).</li>
              <li><code>module_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>module_progress</code>: массив объектов <a href="#schemas-module-progress">ModuleProgress</a>.</li>
              <li><code>quiz_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>quiz_progress</code>: массив объектов <a href="#schemas-quiz-progress">QuizProgress</a>, по одной записи на пару <code>(course_lesson_id, quiz_id)</code> со сводкой по последней non-cancelled попытке ученика.</li>
            </ul>
            <p>Внимание: один и тот же квиз может быть привязан к нескольким урокам одного курса, поэтому <code>quiz_progress</code> адресуется парой урок/квиз, а не одним <code>quiz_id</code>. Клиент сшивает per-user прогресс с деревом по <code>course_nodeble_id</code> lesson/module-узлов и по паре <code>(lesson.course_nodeble_id, quizzes[i].id)</code> для section-узлов.</p>
          uk: |
            <p>Необов'язковий список розширень відповіді через кому. Кожен токен підключає одну секцію відповіді — клієнт платить лише за те, що попросив.</p>
            <ul>
              <li><code>course_program</code> — додає програму курсу на верхньому рівні відповіді у поле <code>course_program</code> (масив вузлів <a href="#schemas-course-program">CourseProgram</a>; у lesson-вузлів у <code>children</code> ідуть <a href="#schemas-course-program-section">CourseProgramSection</a> з прив'язаними квізами). Повертається один раз поряд з <code>data</code>/<code>links</code>/<code>meta</code>, бо програма однакова для всіх учнів сторінки.</li>
              <li><code>lesson_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>lesson_progress</code>: масив об'єктів <a href="#schemas-lesson-progress">LessonProgress</a> (по одному запису на урок, по якому в учня є прогрес).</li>
              <li><code>module_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>module_progress</code>: масив об'єктів <a href="#schemas-module-progress">ModuleProgress</a>.</li>
              <li><code>quiz_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>quiz_progress</code>: масив об'єктів <a href="#schemas-quiz-progress">QuizProgress</a>, по одному запису на пару <code>(course_lesson_id, quiz_id)</code> зі зведенням за останньою non-cancelled спробою учня.</li>
            </ul>
            <p>Увага: один і той самий квіз може бути прив'язаний до кількох уроків одного курсу, тому <code>quiz_progress</code> адресується парою урок/квіз, а не одним <code>quiz_id</code>. Клієнт зшиває per-user прогрес з деревом за <code>course_nodeble_id</code> lesson/module-вузлів і за парою <code>(lesson.course_nodeble_id, quizzes[i].id)</code> для section-вузлів.</p>
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CourseUser"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                  course_program:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CourseProgram"
                    description: "*Conditional:* Returned when <code>include</code>
                      contains <code>course_program</code>. Course program tree returned
                      at the top level when `include` contains `course_program`. Same
                      shape as the existing `CourseProgram` schema, with lesson nodes
                      containing `CourseProgramSection` children. Identical for all
                      users on the page, so the API returns it once instead of repeating
                      it per row."
                    x-translations:
                      ru: "*Conditional:* Returned when <code>include</code> contains
                        <code>course_program</code>. Программа курса, возвращается
                        на верхнем уровне когда `include` содержит `course_program`.
                        Форма полей та же, что у существующей схемы `CourseProgram`,
                        у lesson-узлов в `children` лежат `CourseProgramSection`.
                        Одинакова для всех учеников страницы, поэтому API возвращает
                        её один раз вместо повторения в каждом ряду."
                      uk: "*Conditional:* Returned when <code>include</code> contains
                        <code>course_program</code>. Програма курсу, повертається
                        на верхньому рівні коли `include` містить `course_program`.
                        Форма полів та сама, що в існуючій схемі `CourseProgram`,
                        у lesson-вузлів у `children` лежать `CourseProgramSection`.
                        Однакова для всіх учнів сторінки, тому API повертає її один
                        раз замість повторення в кожному ряду."
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/courses/{course}/users/query":
    post:
      operationId: courses_users_query
      tags:
      - courses
      summary: Query course participants (POST alias for GET /courses/:course/users)
      parameters:
      - name: course
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                search:
                  type: string
                  description: Fuzzy search by email or name
                  x-translations:
                    ru: Нестрогий поиск по имейлу или имени
                    uk: Нестрогий пошук по імейлу або імені
                  example: John
                contact_id:
                  type: integer
                  description: By contact id
                  x-translations:
                    ru: По id контакта
                    uk: По id контакту
                  example: 123
                contact_ids:
                  oneOf:
                  - type: integer
                  - type: array
                    items:
                      type: integer
                  description: Filter by contact ids. Accepts a comma-separated string
                    or a repeated array of integers.
                  x-translations:
                    ru: Фильтр по id контактов. Принимает строку через запятую или
                      повторяющийся массив целых чисел.
                    uk: Фільтр за id контактів. Приймає рядок через кому або повторюваний
                      масив цілих чисел.
                user_id:
                  type: integer
                  description: By user id
                  x-translations:
                    ru: По id пользователя
                    uk: По id користувача
                  example: 456
                user_ids:
                  oneOf:
                  - type: integer
                  - type: array
                    items:
                      type: integer
                  description: Filter by user (student account) ids. Accepts a comma-separated
                    string or a repeated array of integers.
                  x-translations:
                    ru: Фильтр по id юзеров (аккаунтов ученика). Принимает строку
                      через запятую или повторяющийся массив целых чисел.
                    uk: Фільтр за id юзерів (акаунтів учня). Приймає рядок через кому
                      або повторюваний масив цілих чисел.
                emails:
                  oneOf:
                  - type: string
                  - type: array
                    items:
                      type: string
                  description: Filter by exact email address. Accepts a comma-separated
                    string or a repeated array; each value must be a valid email.
                  x-translations:
                    ru: Фильтр по точному email-адресу. Принимает строку через запятую
                      или повторяющийся массив; каждое значение должно быть валидным
                      email.
                    uk: Фільтр за точним email-адресою. Приймає рядок через кому або
                      повторюваний масив; кожне значення має бути валідним email.
                progress_general_from:
                  type: integer
                  description: General progress from (0 to 99)
                  x-translations:
                    ru: Общий прогресс от (от 0 до 99)
                    uk: Загальний прогрес від (від 0 до 99)
                  example: 0
                progress_general_to:
                  type: integer
                  description: General progress to (0 to 100)
                  x-translations:
                    ru: Общий прогресс до (от 0 до 100)
                    uk: Загальний прогрес до (від 0 до 100)
                  example: 100
                lessons_viewed_from:
                  type: integer
                  description: Lesson viewing percentage from (0 to 99)
                  x-translations:
                    ru: Процент просмотра урока от (от 0 до 99)
                    uk: Відсоток перегляду уроку від (від 0 до 99)
                  example: 5
                lessons_viewed_to:
                  type: integer
                  description: Lesson viewing percentage to (0 to 100)
                  x-translations:
                    ru: Процент просмотра урока до (от 0 до 100)
                    uk: Відсоток перегляду уроку до (від 0 до 100)
                  example: 20
                quizzes_progress_from:
                  type: integer
                  description: Quizzes progress percentage from (0 to 99)
                  x-translations:
                    ru: Процент пройденных практик от (от 0 до 99)
                    uk: Відсоток пройдених практик від (від 0 до 99)
                  example: 0
                quizzes_progress_to:
                  type: integer
                  description: Quizzes progress percentage to (0 to 100)
                  x-translations:
                    ru: Процент пройденных практик до (от 0 до 100)
                    uk: Відсоток пройдених практик до (від 0 до 100)
                  example: 100
                last_activity_from:
                  type: string
                  format: date-time
                  description: Last activity on course from (UTC date)
                  x-translations:
                    ru: Последняя активность на курсе от (дата UTC)
                    uk: Остання активність на курсі від (дата UTC)
                last_activity_to:
                  type: string
                  format: date-time
                  description: Last activity on course to (UTC date)
                  x-translations:
                    ru: Последняя активность на курсе до (дата UTC)
                    uk: Остання активність на курсі до (дата UTC)
                per_page:
                  type: integer
                  description: Number of items per page
                  x-translations:
                    ru: Количество элементов на странице
                    uk: Кількість елементів на сторінці
                  default: 15
                  maximum: 250
                  example: 15
                page:
                  type: integer
                  description: Page number
                  x-translations:
                    ru: Номер страницы
                    uk: Номер сторінки
                  default: 1
                  example: 1
                include:
                  oneOf:
                  - type: string
                  - type: array
                    items:
                      type: string
                  description: |
                    <p>Optional comma-separated list of response extensions. Each token enables one section of the response — clients only pay for what they ask for.</p>
                    <ul>
                      <li><code>course_program</code> — adds the course program tree at the top level of the response as <code>course_program</code> (array of <a href="#schemas-course-program">CourseProgram</a> nodes; lesson nodes contain <a href="#schemas-course-program-section">CourseProgramSection</a> children with attached quizzes). Returned once next to <code>data</code>/<code>links</code>/<code>meta</code> because it is the same for every user on the page.</li>
                      <li><code>lesson_progress</code> — adds <code>lesson_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-lesson-progress">LessonProgress</a> objects (one per lesson the user has progress on).</li>
                      <li><code>module_progress</code> — adds <code>module_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-module-progress">ModuleProgress</a> objects.</li>
                      <li><code>quiz_progress</code> — adds <code>quiz_progress</code> inside every <code>data[]</code> row: array of <a href="#schemas-quiz-progress">QuizProgress</a> objects, one per <code>(course_lesson_id, quiz_id)</code> pair summarised from the user's latest non-cancelled attempt.</li>
                    </ul>
                    <p>Note: the same quiz can be attached to several lessons of the same course, so <code>quiz_progress</code> is keyed by the lesson/quiz pair, not by <code>quiz_id</code> alone. The client stitches per-user progress to the program tree by <code>course_nodeble_id</code> of lesson/module nodes and by the <code>(lesson.course_nodeble_id, quizzes[i].id)</code> pair for section nodes.</p>
                  x-translations:
                    ru: |
                      <p>Необязательный список расширений ответа через запятую. Каждый токен подключает одну секцию ответа — клиент платит только за то, что попросил.</p>
                      <ul>
                        <li><code>course_program</code> — добавляет программу курса на верхнем уровне ответа в поле <code>course_program</code> (массив узлов <a href="#schemas-course-program">CourseProgram</a>; у lesson-узлов в <code>children</code> едут <a href="#schemas-course-program-section">CourseProgramSection</a> с привязанными квизами). Возвращается один раз рядом с <code>data</code>/<code>links</code>/<code>meta</code>, потому что программа одинакова для всех учеников страницы.</li>
                        <li><code>lesson_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>lesson_progress</code>: массив объектов <a href="#schemas-lesson-progress">LessonProgress</a> (по одной записи на урок, по которому у ученика есть прогресс).</li>
                        <li><code>module_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>module_progress</code>: массив объектов <a href="#schemas-module-progress">ModuleProgress</a>.</li>
                        <li><code>quiz_progress</code> — добавляет в каждый элемент <code>data[]</code> поле <code>quiz_progress</code>: массив объектов <a href="#schemas-quiz-progress">QuizProgress</a>, по одной записи на пару <code>(course_lesson_id, quiz_id)</code> со сводкой по последней non-cancelled попытке ученика.</li>
                      </ul>
                      <p>Внимание: один и тот же квиз может быть привязан к нескольким урокам одного курса, поэтому <code>quiz_progress</code> адресуется парой урок/квиз, а не одним <code>quiz_id</code>. Клиент сшивает per-user прогресс с деревом по <code>course_nodeble_id</code> lesson/module-узлов и по паре <code>(lesson.course_nodeble_id, quizzes[i].id)</code> для section-узлов.</p>
                    uk: |
                      <p>Необов'язковий список розширень відповіді через кому. Кожен токен підключає одну секцію відповіді — клієнт платить лише за те, що попросив.</p>
                      <ul>
                        <li><code>course_program</code> — додає програму курсу на верхньому рівні відповіді у поле <code>course_program</code> (масив вузлів <a href="#schemas-course-program">CourseProgram</a>; у lesson-вузлів у <code>children</code> ідуть <a href="#schemas-course-program-section">CourseProgramSection</a> з прив'язаними квізами). Повертається один раз поряд з <code>data</code>/<code>links</code>/<code>meta</code>, бо програма однакова для всіх учнів сторінки.</li>
                        <li><code>lesson_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>lesson_progress</code>: масив об'єктів <a href="#schemas-lesson-progress">LessonProgress</a> (по одному запису на урок, по якому в учня є прогрес).</li>
                        <li><code>module_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>module_progress</code>: масив об'єктів <a href="#schemas-module-progress">ModuleProgress</a>.</li>
                        <li><code>quiz_progress</code> — додає у кожен елемент <code>data[]</code> поле <code>quiz_progress</code>: масив об'єктів <a href="#schemas-quiz-progress">QuizProgress</a>, по одному запису на пару <code>(course_lesson_id, quiz_id)</code> зі зведенням за останньою non-cancelled спробою учня.</li>
                      </ul>
                      <p>Увага: один і той самий квіз може бути прив'язаний до кількох уроків одного курсу, тому <code>quiz_progress</code> адресується парою урок/квіз, а не одним <code>quiz_id</code>. Клієнт зшиває per-user прогрес з деревом за <code>course_nodeble_id</code> lesson/module-вузлів і за парою <code>(lesson.course_nodeble_id, quizzes[i].id)</code> для section-вузлів.</p>
            examples:
              with_analytics:
                summary: Query course users with analytics include
                value:
                  include:
                  - course_program
                  - lesson_progress
                  - module_progress
                  - quiz_progress
                  contact_ids:
                  - 1
                  - 2
                  - 3
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CourseUser"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                  course_program:
                    type: array
                    items:
                      "$ref": "#/components/schemas/CourseProgram"
                    description: "*Conditional:* Returned when <code>include</code>
                      contains <code>course_program</code>. Course program tree returned
                      at the top level when `include` contains `course_program`. Same
                      shape as the existing `CourseProgram` schema, with lesson nodes
                      containing `CourseProgramSection` children. Identical for all
                      users on the page, so the API returns it once instead of repeating
                      it per row."
                    x-translations:
                      ru: "*Conditional:* Returned when <code>include</code> contains
                        <code>course_program</code>. Программа курса, возвращается
                        на верхнем уровне когда `include` содержит `course_program`.
                        Форма полей та же, что у существующей схемы `CourseProgram`,
                        у lesson-узлов в `children` лежат `CourseProgramSection`.
                        Одинакова для всех учеников страницы, поэтому API возвращает
                        её один раз вместо повторения в каждом ряду."
                      uk: "*Conditional:* Returned when <code>include</code> contains
                        <code>course_program</code>. Програма курсу, повертається
                        на верхньому рівні коли `include` містить `course_program`.
                        Форма полів та сама, що в існуючій схемі `CourseProgram`,
                        у lesson-вузлів у `children` лежать `CourseProgramSection`.
                        Однакова для всіх учнів сторінки, тому API повертає її один
                        раз замість повторення в кожному ряду."
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/mailing/contact-lists":
    get:
      operationId: mailing_contact_lists_index
      tags:
      - mailing
      summary: Get contact lists
      parameters:
      - name: page
        in: query
        required: false
        schema:
          type: integer
          example: 1
        description: Page Number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          example: 15
        description: Number of fetch items
        x-translations:
          ru: Кол-во элементов выборки
          uk: Кількість елементів вибірки
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/ContactList"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
    post:
      operationId: mailing_contact_lists_store
      tags:
      - mailing
      summary: Create contact list
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: Title
                  x-translations:
                    ru: Название
                    uk: Назва
                  example: Webinar Participants 2024
                description:
                  type: string
                  description: Description
                  x-translations:
                    ru: Описание
                    uk: Опис
                  example: All contacts who registered for webinar
              required:
              - title
            examples:
              basic:
                summary: Create contact list
                value:
                  title: Название группы
                  description: описание
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/ContactList"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/mailing/contact-lists/bulk-contacts":
    post:
      operationId: mailing_contact_lists_bulk_contacts
      tags:
      - mailing
      summary: Bulk add contacts to lists
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                contacts:
                  type: array
                  items:
                    type: object
                  description: Contacts (ID)
                  x-translations:
                    ru: Контакты (ID)
                    uk: Контакти (ID)
                  example:
                  - 1
                  - 2
                  - 15
                  - 42
                contact_lists:
                  type: array
                  items:
                    type: object
                  description: Contact Lists (ID)
                  x-translations:
                    ru: Списки контактов (ID)
                    uk: Списки контактів (ID)
                  example:
                  - 1
                  - 3
              required:
              - contacts
              - contact_lists
            examples:
              basic:
                summary: Bulk add contacts to lists
                value:
                  contacts[]: 2
                  contact_lists[]: 1
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Success"
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/mailing/contact-lists/{id}":
    get:
      operationId: mailing_contact_lists_show
      tags:
      - mailing
      summary: Get contact list by ID
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/ContactList"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/offers":
    get:
      operationId: offers_index
      tags:
      - offers
      summary: Get offers list
      parameters:
      - name: page
        in: query
        required: false
        schema:
          type: integer
          example: 1
        description: Page Number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          example: 15
        description: Number of fetch items
        x-translations:
          ru: Кол-во элементов выборки
          uk: Кількість елементів вибірки
      - name: product_id
        in: query
        required: false
        schema:
          type: integer
          example: 431
        description: Filter by product
        x-translations:
          ru: Фильтр по продукту
          uk: Фільтр по продукту
      - name: filters[search]
        in: query
        required: false
        schema:
          type: string
          example: Premium
        description: Search
        x-translations:
          ru: Поисковый запрос
          uk: Пошуковий запит
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Offer"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/offers/{id}":
    get:
      operationId: offers_show
      tags:
      - offers
      summary: Get offer by ID
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    "$ref": "#/components/schemas/Offer"
                required:
                - data
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/products":
    get:
      operationId: products_index
      tags:
      - products
      summary: Get products list
      parameters:
      - name: sort_by
        in: query
        required: false
        schema:
          type: string
          default: desc
          enum:
          - asc
          - desc
      - name: per_page
        in: query
        required: false
        schema:
          type: integer
          default: 15
          example: 15
        description: Number of items per page
        x-translations:
          ru: Количество элементов на странице
          uk: Кількість елементів на сторінці
      - name: page
        in: query
        required: false
        schema:
          type: integer
          default: 1
          example: 1
        description: Page number
        x-translations:
          ru: Номер страницы
          uk: Номер сторінки
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Product"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
  "/timezones":
    get:
      operationId: timezones_index
      tags:
      - timezones
      summary: Get timezones list
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      "$ref": "#/components/schemas/Timezone"
                  links:
                    "$ref": "#/components/schemas/PaginationLinks"
                  meta:
                    "$ref": "#/components/schemas/PaginationMeta"
                required:
                - data
                - meta
        '401':
          description: Unauthorized — missing or invalid Token
        '404':
          description: Not Found
        '422':
          description: Validation error
        '429':
          description: Too Many Requests — rate limit exceeded
components:
  securitySchemes:
    TokenAuth:
      type: apiKey
      in: header
      name: Token
    CabinetHash:
      type: apiKey
      in: header
      name: Cabinet-Hash
  schemas:
    CertificateTemplate:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Internal template title
          x-translations:
            ru: Внутреннее название шаблона
            uk: Внутрішня назва шаблону
        public_title:
          type: string
          description: Public-facing template title shown on the issued certificate
          x-translations:
            ru: Публичное название шаблона, отображаемое на выпущенном сертификате
            uk: Публічна назва шаблону, що відображається на випущеному сертифікаті
          nullable: true
      required:
      - id
      - title
      - public_title
    Certificate:
      type: object
      properties:
        id:
          type: integer
        uuid:
          type: string
          description: Universally unique identifier of the certificate
          x-translations:
            ru: Универсальный уникальный идентификатор сертификата
            uk: Універсальний унікальний ідентифікатор сертифіката
        cabinet_id:
          type: integer
        user_id:
          type: integer
          nullable: true
        number:
          type: string
          description: Public certificate number used for lookup
          x-translations:
            ru: Публичный номер сертификата для поиска
            uk: Публічний номер сертифіката для пошуку
          example: KW-2024-00042
        link:
          type: string
          description: Public verification link; `null` until the certificate is issued
          x-translations:
            ru: Публичная ссылка для проверки; `null` пока сертификат не выпущен
            uk: Публічне посилання для перевірки; `null` поки сертифікат не випущено
          nullable: true
        issued_at:
          type: string
          format: date-time
          nullable: true
        finished_at:
          type: string
          format: date-time
          description: "*Conditional:* Included when both the user and the certificate
            target are available in the response. When the user completed the certificateble
            entity (course, etc.)"
          x-translations:
            ru: "*Conditional:* Included when both the user and the certificate target
              are available in the response. Когда пользователь завершил certificateble-сущность
              (курс и т.п.)"
            uk: "*Conditional:* Included when both the user and the certificate target
              are available in the response. Коли користувач завершив certificateble-сутність
              (курс тощо)"
          nullable: true
        points:
          type: integer
          description: "*Conditional:* Included when both the user and the certificate
            target are available in the response. Points earned by the user toward
            the certificateble"
          x-translations:
            ru: "*Conditional:* Included when both the user and the certificate target
              are available in the response. Баллы, набранные пользователем для получения
              сертификата"
            uk: "*Conditional:* Included when both the user and the certificate target
              are available in the response. Бали, набрані користувачем для отримання
              сертифіката"
          nullable: true
        user:
          allOf:
          - "$ref": "#/components/schemas/UserSimple"
          description: "*Conditional:* Included when the related user is part of the
            response."
        creator:
          allOf:
          - "$ref": "#/components/schemas/UserSimple"
          description: "*Conditional:* Included when creator information is part of
            the response."
        certificate_template:
          allOf:
          - "$ref": "#/components/schemas/CertificateTemplate"
          description: "*Conditional:* Included when the certificate template is part
            of the response."
        certificateble_title:
          type: string
          description: Title of the entity the certificate is issued for (course,
            product, etc.)
          x-translations:
            ru: Название сущности, на которую выпущен сертификат (курс, продукт и
              т.п.)
            uk: Назва сутності, на яку випущено сертифікат (курс, продукт тощо)
      required:
      - id
      - uuid
      - cabinet_id
      - user_id
      - number
      - link
      - issued_at
      - certificateble_title
    StudentProductCertificateInfo:
      type: object
      properties:
        type:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Certificate type (enum payload with id/title)
          x-translations:
            ru: Тип сертификата (enum-payload с id/title)
            uk: Тип сертифіката (enum-payload з id/title)
        certificate_template:
          allOf:
          - "$ref": "#/components/schemas/CertificateTemplate"
          description: "*Conditional:* Included when the certificate template is part
            of the response."
        certificateble_type:
          type: string
          description: Polymorphic type of the certificateble entity (e.g. course,
            product)
          x-translations:
            ru: Полиморфный тип certificateble-сущности (курс, продукт и т.п.)
            uk: Поліморфний тип certificateble-сутності (курс, продукт тощо)
        certificateble_id:
          type: integer
        certificateble_title:
          type: string
          description: "*Conditional:* Included when the certificate target is part
            of the response. Title of the certificateble entity"
          x-translations:
            ru: "*Conditional:* Included when the certificate target is part of the
              response. Название certificateble-сущности"
            uk: "*Conditional:* Included when the certificate target is part of the
              response. Назва certificateble-сутності"
        message:
          type: string
          nullable: true
        system_comment:
          type: string
          nullable: true
        certificate:
          allOf:
          - "$ref": "#/components/schemas/Certificate"
          description: "*Conditional:* Returned only after the certificate has been
            issued."
      required:
      - type
      - certificateble_type
      - certificateble_id
      - message
      - system_comment
    CommentIdentifier:
      type: object
      properties:
        id:
          type: integer
        text:
          type: string
          description: Raw comment text
          x-translations:
            ru: Сырой текст комментария
            uk: Сирий текст коментаря
        commentable_id:
          type: integer
          description: Identifier of the entity the comment is attached to
          x-translations:
            ru: Идентификатор сущности, к которой прикреплён комментарий
            uk: Ідентифікатор сутності, до якої прикріплено коментар
        commentable_type:
          type: string
          description: Polymorphic type of the target entity
          x-translations:
            ru: Полиморфный тип целевой сущности
            uk: Поліморфний тип цільової сутності
        commentable:
          type: object
          description: "*Conditional:* Included when the target entity is part of
            the response. Compact payload of the target entity"
          x-translations:
            ru: "*Conditional:* Included when the target entity is part of the response.
              Краткое представление целевой сущности"
            uk: "*Conditional:* Included when the target entity is part of the response.
              Короткий payload цільової сутності"
      required:
      - id
      - text
      - commentable_id
      - commentable_type
    CountrySimple:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
          description: ISO 3166-1 alpha-2 country code
          x-translations:
            ru: ISO 3166-1 alpha-2 код страны
            uk: ISO 3166-1 alpha-2 код країни
          example: UA
        name:
          type: string
          description: Localized country name in the current `X-Language`
          x-translations:
            ru: Локализованное название страны в текущем `X-Language`
            uk: Локалізована назва країни в поточному `X-Language`
          example: Ukraine
      required:
      - id
      - code
      - name
    DateSetting:
      type: object
      properties:
        type:
          type: string
          description: Setting type — `fixed`, `relative`, `recurring`, etc.
          x-translations:
            ru: Тип настройки — `fixed`, `relative`, `recurring` и т.д.
            uk: Тип налаштування — `fixed`, `relative`, `recurring` тощо
        duration_type_id:
          type: integer
          description: Identifier of the duration unit (days/weeks/months)
          x-translations:
            ru: Идентификатор единицы длительности (дни/недели/месяцы)
            uk: Ідентифікатор одиниці тривалості (дні/тижні/місяці)
          nullable: true
        date_at:
          type: string
          format: date-time
          description: Fixed date in the cabinet timezone
          x-translations:
            ru: Фиксированная дата в таймзоне кабинета
            uk: Фіксована дата в таймзоні кабінету
          nullable: true
        date_at_utc:
          type: string
          format: date-time
          description: Same date converted to UTC
          x-translations:
            ru: Та же дата, переведённая в UTC
            uk: Та сама дата, переведена в UTC
          nullable: true
        timezone_id:
          type: integer
          nullable: true
        timezone:
          allOf:
          - "$ref": "#/components/schemas/Timezone"
          nullable: true
        after_months:
          type: integer
          description: Relative offset in months (for relative types)
          x-translations:
            ru: Относительное смещение в месяцах (для relative-типов)
            uk: Відносний зсув у місяцях (для relative-типів)
          nullable: true
        after_days:
          type: integer
          nullable: true
        specific_time:
          type: string
          description: Specific time of day (HH:MM)
          x-translations:
            ru: Конкретное время дня (HH:MM)
            uk: Конкретний час дня (HH:MM)
          nullable: true
        specific_day_of_week:
          type: integer
          description: 1 (Monday) – 7 (Sunday) when bound to a specific weekday
          x-translations:
            ru: 1 (понедельник) – 7 (воскресенье), если привязано к конкретному дню
              недели
            uk: 1 (понеділок) – 7 (неділя), якщо привʼязано до конкретного дня тижня
          nullable: true
        is_current_week:
          type: boolean
          nullable: true
        is_current_day:
          type: boolean
          nullable: true
      required:
      - type
      - duration_type_id
      - date_at
      - date_at_utc
      - timezone_id
      - timezone
      - after_months
      - after_days
      - specific_time
      - specific_day_of_week
      - is_current_week
      - is_current_day
    EnumValue:
      type: object
      properties:
        id:
          type: integer
          description: Numeric backing value of the enum case (stable across releases).
          x-translations:
            ru: Числовое значение enum-кейса (стабильно между релизами).
            uk: Числове значення enum-кейса (стабільне між релізами).
        slug:
          type: string
          description: PHP case name in PascalCase. Stable machine key for client
            switch/match logic.
          x-translations:
            ru: PHP-имя кейса в PascalCase. Стабильный машинный ключ для логики switch/match
              на клиенте.
            uk: PHP-ім'я кейса у PascalCase. Стабільний машинний ключ для логіки switch/match
              на клієнті.
        title:
          type: string
          description: Localized label. Depends on the Accept-Language header.
          x-translations:
            ru: Локализованный заголовок. Зависит от заголовка Accept-Language запроса.
            uk: Локалізований заголовок. Залежить від заголовка Accept-Language запиту.
      required:
      - id
      - slug
      - title
    Enum:
      type: object
      properties:
        id:
          type: integer
          description: Numeric enum value (also referred to as `value` in some places)
          x-translations:
            ru: Числовое значение enum (в некоторых местах называется `value`)
            uk: Числове значення enum (в деяких місцях називається `value`)
          example: 3
        slug:
          type: string
          description: Enum case name (stable string identifier)
          x-translations:
            ru: Имя enum-кейса (стабильный строковый идентификатор)
            uk: Імʼя enum-кейса (стабільний рядковий ідентифікатор)
          example: completed
        title:
          type: string
          description: Localized enum label rendered in the current `X-Language`
          x-translations:
            ru: Локализованная подпись enum в текущем `X-Language`
            uk: Локалізований підпис enum у поточному `X-Language`
          example: Completed
      required:
      - id
      - slug
      - title
    FileSimple:
      type: object
      properties:
        id:
          type: integer
        uuid:
          type: string
          description: Universally unique identifier of the file
          x-translations:
            ru: Универсальный уникальный идентификатор файла
            uk: Універсальний унікальний ідентифікатор файлу
        name:
          type: string
          description: Internal stored name of the file
          x-translations:
            ru: Внутреннее сохранённое имя файла
            uk: Внутрішнє збережене ім'я файлу
        original_name:
          type: string
          description: Original filename uploaded by the user
          x-translations:
            ru: Оригинальное имя файла, загруженного пользователем
            uk: Оригінальне ім'я файлу, завантаженого користувачем
        url:
          type: string
          description: Public URL to the file (null when storage is unavailable or
            the viewer lacks access)
          x-translations:
            ru: Публичная ссылка на файл (null, если хранилище недоступно или у читателя
              нет доступа)
            uk: Публічне посилання на файл (null, якщо сховище недоступне або у читача
              немає доступу)
          nullable: true
          example: https://files.kwiga.com/cabinets/42/preview.jpg
        thumbnails:
          type: object
          description: Map of thumbnail sizes to public URLs
          x-translations:
            ru: Карта размеров превью на публичные ссылки
            uk: Карта розмірів прев'ю на публічні посилання
          nullable: true
        extension:
          type: string
          description: File extension without the leading dot
          x-translations:
            ru: Расширение файла без ведущей точки
            uk: Розширення файлу без провідної крапки
          nullable: true
          example: jpg
        type_id:
          type: integer
          description: File type identifier
          x-translations:
            ru: Идентификатор типа файла
            uk: Ідентифікатор типу файлу
        mime_type:
          type: string
          nullable: true
          example: image/jpeg
      required:
      - id
      - uuid
      - name
      - original_name
      - url
      - thumbnails
      - extension
      - type_id
      - mime_type
    Timezone:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          description: IANA timezone identifier
          x-translations:
            ru: Идентификатор таймзоны IANA
            uk: Ідентифікатор таймзони IANA
          example: Europe/Kyiv
        name_full:
          type: string
          description: Timezone name with UTC offset for display
          x-translations:
            ru: Название таймзоны со смещением UTC для отображения
            uk: Назва таймзони зі зміщенням UTC для відображення
          example: Europe/Kyiv (UTC+02:00)
        value:
          type: string
          description: UTC offset string
          x-translations:
            ru: Строка UTC-смещения
            uk: Рядок UTC-зміщення
          example: UTC+02:00
      required:
      - id
      - name
      - name_full
      - value
    UserSimple:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          description: Composed full name (first + last) of the user
          x-translations:
            ru: Полное имя пользователя (имя + фамилия)
            uk: Повне імʼя користувача (імʼя + прізвище)
          nullable: true
        email:
          type: string
          description: User email (may be masked for non-owner viewers)
          x-translations:
            ru: Email пользователя (может быть скрыт от не-владельцев)
            uk: Email користувача (може бути приховано від не-власників)
          nullable: true
          example: user@example.com
      required:
      - id
      - name
      - email
    ContactAdditionalField:
      type: object
      properties:
        field:
          allOf:
          - "$ref": "#/components/schemas/ContactField"
          description: "*Conditional:* Included when the field definition is part
            of the response. Definition of the cabinet-specific custom field"
          x-translations:
            ru: "*Conditional:* Included when the field definition is part of the
              response. Определение кастомного поля кабинета"
            uk: "*Conditional:* Included when the field definition is part of the
              response. Визначення кастомного поля кабінету"
        value:
          type: string
          description: Raw value stored for this field on the contact
          x-translations:
            ru: Сырое значение, сохранённое для этого поля на контакте
            uk: Сире значення, збережене для цього поля на контакті
          nullable: true
        display_value:
          type: string
          description: Human-readable rendering of the value (e.g. localized enum
            label)
          x-translations:
            ru: Читаемое отображение значения (например, локализованный enum-label)
            uk: Читаюче відображення значення (наприклад, локалізований enum-label)
          nullable: true
      required:
      - value
      - display_value
    ContactField:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Localized field title
          x-translations:
            ru: Локализованное название поля
            uk: Локалізована назва поля
        is_local:
          type: boolean
          description: Whether the field is cabinet-local (not part of the global
            schema)
          x-translations:
            ru: Является ли поле локальным для кабинета (не частью глобальной схемы)
            uk: Чи є поле локальним для кабінету (не частиною глобальної схеми)
        input_title:
          type: string
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        format:
          type: string
          description: "*Conditional:* Returned only for viewers with extended access
            to the field. Field input format token"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended access to
              the field. Токен формата ввода для поля"
            uk: "*Conditional:* Returned only for viewers with extended access to
              the field. Токен формату вводу для поля"
        validation:
          type: string
          description: "*Conditional:* Returned only for viewers with extended access
            to the field. Validation rule expression"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended access to
              the field. Выражение правила валидации"
            uk: "*Conditional:* Returned only for viewers with extended access to
              the field. Вираз правила валідації"
        default_value:
          type: string
          nullable: true
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        placeholder:
          type: string
          nullable: true
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        type:
          type: string
          description: "*Conditional:* Returned only for viewers with extended access
            to the field. Field type identifier (text, number, date, enum, etc.)"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended access to
              the field. Идентификатор типа поля (text, number, date, enum и т.д.)"
            uk: "*Conditional:* Returned only for viewers with extended access to
              the field. Ідентифікатор типу поля (text, number, date, enum тощо)"
        is_default:
          type: boolean
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        is_required:
          type: boolean
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        is_at_import:
          type: boolean
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        visible_type:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: "*Conditional:* Returned only for viewers with extended access
            to the field."
        order:
          type: integer
          description: "*Conditional:* Returned only for viewers with extended access
            to the field. Display order within the cabinet"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended access to
              the field. Порядок отображения внутри кабинета"
            uk: "*Conditional:* Returned only for viewers with extended access to
              the field. Порядок відображення всередині кабінету"
        values:
          type: array
          items:
            type: object
          description: "*Conditional:* Returned for viewers with extended access when
            value options are configured. Allowed values for enum-style fields"
          x-translations:
            ru: "*Conditional:* Returned for viewers with extended access when value
              options are configured. Допустимые значения для enum-полей"
            uk: "*Conditional:* Returned for viewers with extended access when value
              options are configured. Допустимі значення для enum-полів"
        name_order_type:
          type: object
          description: "*Conditional:* Returned for viewers with extended access when
            the field is the default full-name field. Cabinet-specific ordering for
            first/last name display"
          x-translations:
            ru: "*Conditional:* Returned for viewers with extended access when the
              field is the default full-name field. Порядок отображения имени/фамилии,
              заданный в кабинете"
            uk: "*Conditional:* Returned for viewers with extended access when the
              field is the default full-name field. Порядок відображення імені/прізвища,
              заданий у кабінеті"
      required:
      - id
      - title
      - is_local
    ContactIdentifier:
      type: object
      properties:
        id:
          type: integer
        user_id:
          type: integer
          nullable: true
        first_name:
          type: string
          nullable: true
        last_name:
          type: string
          nullable: true
        name:
          type: string
          description: Composed full name (first + last) when set
          x-translations:
            ru: Полное имя контакта (имя + фамилия), если указано
            uk: Повне ім'я контакту (ім'я + прізвище), якщо вказано
          nullable: true
        email:
          type: string
          description: Contact email (may be masked for non-owner viewers)
          x-translations:
            ru: Email контакта (может быть скрыт для не-владельцев)
            uk: Email контакту (може бути прихований для не-власників)
          nullable: true
        phone:
          type: string
          description: Contact phone (may be masked for non-owner viewers)
          x-translations:
            ru: Телефон контакта (может быть скрыт для не-владельцев)
            uk: Телефон контакту (може бути прихований для не-власників)
          nullable: true
      required:
      - id
      - user_id
      - first_name
      - last_name
      - name
      - email
      - phone
    ContactListStatistic:
      type: object
      properties:
        contact_list_id:
          type: integer
        count_contacts:
          type: integer
          description: Number of contacts currently in the list
          x-translations:
            ru: Количество контактов, сейчас находящихся в списке
            uk: Кількість контактів, які зараз у списку
      required:
      - contact_list_id
      - count_contacts
    ContactList:
      type: object
      properties:
        id:
          type: integer
        cabinet_id:
          type: integer
          description: "*Conditional:* Included when the cabinet relation is part
            of the response. Owning cabinet identifier"
          x-translations:
            ru: "*Conditional:* Included when the cabinet relation is part of the
              response. Идентификатор кабинета-владельца"
            uk: "*Conditional:* Included when the cabinet relation is part of the
              response. Ідентифікатор кабінету-власника"
        user_id:
          type: integer
          description: "*Conditional:* Included when the owning user is part of the
            response. Owning user identifier"
          x-translations:
            ru: "*Conditional:* Included when the owning user is part of the response.
              Идентификатор пользователя-владельца"
            uk: "*Conditional:* Included when the owning user is part of the response.
              Ідентифікатор користувача-власника"
        title:
          type: string
        description:
          type: string
          nullable: true
        is_default:
          type: boolean
          description: True for the cabinet's built-in default list
          x-translations:
            ru: True для встроенного списка по умолчанию в кабинете
            uk: True для вбудованого списку за замовчуванням у кабінеті
        is_active:
          type: boolean
        is_segment:
          type: boolean
          description: True when the list is a dynamic segment driven by filters
          x-translations:
            ru: True, если список — динамический сегмент, управляемый фильтрами
            uk: True, якщо список — динамічний сегмент, керований фільтрами
        is_new:
          type: boolean
          description: Marker for lists created after 2023-08-15 (used by the UI)
          x-translations:
            ru: Маркер для списков, созданных после 2023-08-15 (используется в UI)
            uk: Маркер для списків, створених після 2023-08-15 (використовується в
              UI)
        paused_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        contacts_count:
          type: integer
          description: "*Conditional:* Included when contact counts are available
            for the list."
        contacts:
          type: array
          items:
            "$ref": "#/components/schemas/ContactIdentifier"
          description: "*Conditional:* Returned on the single-list endpoint; not present
            on the list response."
        statistic:
          allOf:
          - "$ref": "#/components/schemas/ContactListStatistic"
          description: "*Conditional:* Included when list statistics are part of the
            response."
        email_campaigns:
          type: array
          items:
            "$ref": "#/components/schemas/EmailCampaignIdentifier"
          description: "*Conditional:* Included when the list's email campaigns are
            part of the response. Email campaigns associated with the list"
          x-translations:
            ru: "*Conditional:* Included when the list's email campaigns are part
              of the response. Email-рассылки, связанные со списком"
            uk: "*Conditional:* Included when the list's email campaigns are part
              of the response. Email-розсилки, пов'язані зі списком"
        segment_settings:
          type: object
          description: "*Conditional:* Present for dynamic segments; empty object
            for regular lists. Filter rules driving the segment membership"
          x-translations:
            ru: "*Conditional:* Present for dynamic segments; empty object for regular
              lists. Правила фильтрации, определяющие членство в сегменте"
            uk: "*Conditional:* Present for dynamic segments; empty object for regular
              lists. Правила фільтрації, що визначають членство в сегменті"
      required:
      - id
      - title
      - description
      - is_default
      - is_active
      - is_segment
      - is_new
      - paused_at
      - created_at
      - updated_at
    Contact:
      type: object
      properties:
        id:
          type: integer
          description: Unique contact identifier
          x-translations:
            ru: Уникальный идентификатор контакта
            uk: Унікальний ідентифікатор контакту
          example: 42
        user_id:
          type: integer
          description: Underlying user identifier (null when the contact has no associated
            user account yet)
          x-translations:
            ru: Идентификатор связанного пользователя (null, если у контакта ещё нет
              аккаунта пользователя)
            uk: Ідентифікатор пов'язаного користувача (null, якщо у контакту ще немає
              акаунту користувача)
          nullable: true
          example: 1024
        email:
          type: string
          description: Contact email address
          x-translations:
            ru: Email контакта
            uk: Email контакту
          example: user@example.com
        first_name:
          type: string
          nullable: true
          example: John
        middle_name:
          type: string
          nullable: true
          example: Michael
        last_name:
          type: string
          nullable: true
          example: Doe
        name:
          type: string
          description: Display name assembled from first/middle/last names in the
            cabinet's preferred order.
          x-translations:
            ru: Отображаемое имя, собранное из имени/отчества/фамилии в предпочтительном
              для кабинета порядке.
            uk: Відображуване ім'я, зібране з імені/по-батькові/прізвища у порядку,
              прийнятому в кабінеті.
          nullable: true
          example: John Michael Doe
        phone:
          type: string
          nullable: true
        crm_link:
          type: string
          description: Direct URL to the contact's profile page in your cabinet's
            CRM.
          x-translations:
            ru: Прямая ссылка на страницу контакта в CRM вашего кабинета.
            uk: Пряме посилання на сторінку контакту у CRM вашого кабінету.
          nullable: true
          example: "+380931234567"
        created_at:
          type: string
          format: date-time
          description: Timestamp when the contact was created
          x-translations:
            ru: Момент создания контакта
            uk: Момент створення контакту
          example: '2024-01-15T10:00:00Z'
        tags:
          type: array
          items:
            "$ref": "#/components/schemas/FlowTag"
          description: "*Conditional:* Included when tags are loaded for the contact.
            Tags attached to the contact"
          x-translations:
            ru: "*Conditional:* Included when tags are loaded for the contact. Теги,
              привязанные к контакту"
            uk: "*Conditional:* Included when tags are loaded for the contact. Теги,
              прикріплені до контакту"
        last_activity_at:
          type: string
          format: date-time
          description: "*Conditional:* Included when the contact has activity history
            in the cabinet. Timestamp of the contact's last activity within the cabinet"
          x-translations:
            ru: "*Conditional:* Included when the contact has activity history in
              the cabinet. Момент последней активности контакта в кабинете"
            uk: "*Conditional:* Included when the contact has activity history in
              the cabinet. Момент останньої активності контакту в кабінеті"
          nullable: true
        first_visit:
          allOf:
          - "$ref": "#/components/schemas/Visit"
          description: "*Conditional:* Included when the contact has at least one
            recorded visit. First recorded visit of the contact"
          x-translations:
            ru: "*Conditional:* Included when the contact has at least one recorded
              visit. Первый зафиксированный визит контакта"
            uk: "*Conditional:* Included when the contact has at least one recorded
              visit. Перший зафіксований візит контакту"
        utm:
          allOf:
          - "$ref": "#/components/schemas/UtmList"
          description: "*Conditional:* Included when UTM data has been collected for
            the contact. Aggregated UTM attribution across the contact's visits"
          x-translations:
            ru: "*Conditional:* Included when UTM data has been collected for the
              contact. Агрегированная UTM-атрибуция по всем визитам контакта"
            uk: "*Conditional:* Included when UTM data has been collected for the
              contact. Агрегована UTM-атрибуція за всіма візитами контакту"
        utm_visits:
          type: array
          items:
            "$ref": "#/components/schemas/Visit"
          description: "*Conditional:* Returned only on `GET /contacts/{id}` — not
            present on the contacts list. All UTM-tracked visits of the contact"
          x-translations:
            ru: "*Conditional:* Returned only on `GET /contacts/{id}` — not present
              on the contacts list. Все UTM-отслеженные визиты контакта"
            uk: "*Conditional:* Returned only on `GET /contacts/{id}` — not present
              on the contacts list. Усі UTM-відстежені візити контакту"
        offers:
          type: array
          items:
            "$ref": "#/components/schemas/Offer"
          description: "*Conditional:* Included when `with_orders=true` is passed.
            Offers the contact has purchased"
          x-translations:
            ru: "*Conditional:* Included when `with_orders=true` is passed. Предложения,
              купленные контактом"
            uk: "*Conditional:* Included when `with_orders=true` is passed. Пропозиції,
              куплені контактом"
        orders:
          type: array
          items:
            "$ref": "#/components/schemas/Order"
          description: "*Conditional:* Included when `with_orders=true` is passed.
            Orders placed by the contact"
          x-translations:
            ru: "*Conditional:* Included when `with_orders=true` is passed. Заказы,
              размещённые контактом"
            uk: "*Conditional:* Included when `with_orders=true` is passed. Замовлення,
              розміщені контактом"
        additional_fields:
          type: array
          items:
            "$ref": "#/components/schemas/ContactAdditionalField"
          description: "*Conditional:* Included when the contact has custom field
            values configured. Cabinet-specific custom fields filled in for this contact"
          x-translations:
            ru: "*Conditional:* Included when the contact has custom field values
              configured. Кастомные поля кабинета, заполненные для этого контакта"
            uk: "*Conditional:* Included when the contact has custom field values
              configured. Кастомні поля кабінету, заповнені для цього контакту"
        student_product_certificate_info:
          type: array
          items:
            "$ref": "#/components/schemas/StudentProductCertificateInfo"
          description: "*Conditional:* Included when `with_certificates=true` is passed.
            Certificate issuance details per product the contact owns"
          x-translations:
            ru: "*Conditional:* Included when `with_certificates=true` is passed.
              Информация о выпущенных сертификатах по продуктам контакта"
            uk: "*Conditional:* Included when `with_certificates=true` is passed.
              Інформація про випущені сертифікати за продуктами контакту"
      required:
      - id
      - user_id
      - email
      - first_name
      - middle_name
      - last_name
      - name
      - phone
      - crm_link
      - created_at
    FlowTag:
      type: object
      properties:
        id:
          type: integer
          example: 17
        name:
          type: string
          example: vip
        contacts_count:
          type: integer
          description: "*Conditional:* Included when contact counts are available
            for the tag. Number of contacts the tag is attached to"
          x-translations:
            ru: "*Conditional:* Included when contact counts are available for the
              tag. Сколько контактов помечено этим тегом"
            uk: "*Conditional:* Included when contact counts are available for the
              tag. Скільки контактів позначено цим тегом"
          example: 123
      required:
      - id
      - name
    CouponCheck:
      type: object
      properties:
        can_use:
          type: boolean
          description: Whether the coupon is valid and may be applied to an order
          x-translations:
            ru: Можно ли применить купон к заказу
            uk: Чи можна застосувати купон до замовлення
        discount:
          type: number
          description: Computed discount amount when `price` was sent in the request;
            otherwise null
          x-translations:
            ru: Вычисленная сумма скидки, если в запросе передавался `price`; иначе
              null
            uk: Обчислена сума знижки, якщо у запиті передавався `price`; інакше null
          nullable: true
          example: 25.5
      required:
      - can_use
      - discount
    CouponType:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Localized coupon type title
          x-translations:
            ru: Локализованное название типа купона
            uk: Локалізована назва типу купона
      required:
      - id
      - title
    ExpertCoupon:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
          description: Coupon code that customers enter at checkout
          x-translations:
            ru: Код купона, который покупатели вводят при оформлении
            uk: Код купона, який покупці вводять при оформленні
          example: SUMMER25
        reward:
          type: number
          description: Reward value (discount amount or percentage depending on `is_fixed`)
          x-translations:
            ru: Значение вознаграждения (сумма скидки или процент в зависимости от
              `is_fixed`)
            uk: Значення винагороди (сума знижки або відсоток залежно від `is_fixed`)
          nullable: true
        type_discount:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Discount type enum payload
          x-translations:
            ru: Тип скидки (enum-payload)
            uk: Тип знижки (enum-payload)
        type_date_expired:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Expiration-rule type enum payload
          x-translations:
            ru: Тип правила истечения (enum-payload)
            uk: Тип правила закінчення (enum-payload)
        type_total_count_used:
          "$ref": "#/components/schemas/Enum"
        type_used_contact_lists:
          "$ref": "#/components/schemas/Enum"
        is_fixed:
          type: boolean
          description: True for fixed-currency discount, false for percentage
          x-translations:
            ru: True для скидки в валюте, false для процентной
            uk: True для знижки у валюті, false для відсоткової
        is_active:
          type: boolean
        used_amount:
          type: integer
          description: Number of times the coupon has been redeemed
          x-translations:
            ru: Сколько раз купон был использован
            uk: Скільки разів купон було використано
        has_infinite_used:
          type: boolean
        has_access_contact_lists:
          type: boolean
          description: Whether coupon use is restricted to specific contact lists
          x-translations:
            ru: Ограничено ли использование купона определёнными списками контактов
            uk: Чи обмежено використання купона певними списками контактів
        total_uses:
          type: integer
          description: Maximum number of times the coupon can be used in total
          x-translations:
            ru: Максимальное общее число использований купона
            uk: Максимальна загальна кількість використань купона
          nullable: true
        total_uses_per_user:
          type: integer
          description: Maximum number of uses per single contact
          x-translations:
            ru: Максимум использований на одного контакта
            uk: Максимум використань на один контакт
          nullable: true
        contact_lists:
          type: array
          items:
            "$ref": "#/components/schemas/ContactList"
          description: "*Conditional:* Included when the coupon's contact-list restrictions
            are part of the response."
        type:
          allOf:
          - "$ref": "#/components/schemas/CouponType"
          description: "*Conditional:* Included when the coupon-type metadata is part
            of the response. Coupon type metadata"
          x-translations:
            ru: "*Conditional:* Included when the coupon-type metadata is part of
              the response. Метаданные типа купона"
            uk: "*Conditional:* Included when the coupon-type metadata is part of
              the response. Метадані типу купона"
        expert:
          allOf:
          - "$ref": "#/components/schemas/UserSimple"
          description: "*Conditional:* Included when the expert who owns the coupon
            is part of the response."
        user:
          allOf:
          - "$ref": "#/components/schemas/UserSimple"
          description: "*Conditional:* Included when the issuing user is part of the
            response."
        contact_id:
          type: integer
          nullable: true
        contact:
          allOf:
          - "$ref": "#/components/schemas/ContactIdentifier"
          description: "*Conditional:* Included when the linked contact is part of
            the response. Compact identity payload of the linked contact"
          x-translations:
            ru: "*Conditional:* Included when the linked contact is part of the response.
              Краткие идентификационные данные связанного контакта"
            uk: "*Conditional:* Included when the linked contact is part of the response.
              Короткі ідентифікаційні дані пов'язаного контакту"
        offers:
          type: array
          items:
            "$ref": "#/components/schemas/OfferIdentifier"
          description: "*Conditional:* Included when the offers limited by this coupon
            are part of the response. Identity payloads of offers limited by this
            coupon"
          x-translations:
            ru: "*Conditional:* Included when the offers limited by this coupon are
              part of the response. Идентификационные данные предложений, на которые
              действует купон"
            uk: "*Conditional:* Included when the offers limited by this coupon are
              part of the response. Ідентифікаційні дані пропозицій, на які діє купон"
        expires_at:
          type: string
          format: date-time
          nullable: true
        expires_at_utc:
          type: string
          format: date-time
          nullable: true
        timezone_id:
          type: integer
          nullable: true
        timezone:
          allOf:
          - "$ref": "#/components/schemas/Timezone"
          description: "*Conditional:* Included when the coupon has timezone settings
            configured."
        comment:
          allOf:
          - "$ref": "#/components/schemas/CommentIdentifier"
          description: "*Conditional:* Included when the latest internal comment is
            part of the response. Latest internal comment payload (admin-facing)"
          x-translations:
            ru: "*Conditional:* Included when the latest internal comment is part
              of the response. Последний внутренний комментарий (для администраторов)"
            uk: "*Conditional:* Included when the latest internal comment is part
              of the response. Останній внутрішній коментар (для адміністраторів)"
        currency_id:
          type: integer
          nullable: true
        currency:
          allOf:
          - "$ref": "#/components/schemas/Currency"
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        deleted_at:
          type: string
          format: date-time
          nullable: true
      required:
      - id
      - code
      - reward
      - type_discount
      - type_date_expired
      - type_total_count_used
      - type_used_contact_lists
      - is_fixed
      - is_active
      - used_amount
      - has_infinite_used
      - has_access_contact_lists
      - total_uses
      - total_uses_per_user
      - contact_id
      - expires_at
      - expires_at_utc
      - timezone_id
      - currency_id
      - currency
      - created_at
      - updated_at
      - deleted_at
    CourseIdentify:
      type: object
      properties:
        id:
          type: integer
        product_id:
          type: integer
          description: Linked product identifier
          x-translations:
            ru: Идентификатор связанного продукта
            uk: Ідентифікатор пов'язаного продукту
        type_id:
          type: integer
          description: Course type identifier (course / marathon / etc.)
          x-translations:
            ru: Идентификатор типа курса (course / marathon / и т.д.)
            uk: Ідентифікатор типу курсу (course / marathon / тощо)
        title:
          type: string
        lessons:
          type: array
          items:
            "$ref": "#/components/schemas/CourseLessonIdentify"
          description: "*Conditional:* Included when the course's lesson identity
            payloads are loaded. Compact identity entries for the course's lessons"
          x-translations:
            ru: "*Conditional:* Included when the course's lesson identity payloads
              are loaded. Краткие идентификационные записи уроков курса"
            uk: "*Conditional:* Included when the course's lesson identity payloads
              are loaded. Короткі ідентифікаційні записи уроків курсу"
        link:
          type: string
          description: Public URL to the course landing page
          x-translations:
            ru: Публичная ссылка на лендинг курса
            uk: Публічне посилання на лендінг курсу
        preview_url:
          type: string
          description: "*Conditional:* Included when the course preview is loaded.
            URL to the course preview image (or default placeholder when missing)"
          x-translations:
            ru: "*Conditional:* Included when the course preview is loaded. URL превью-картинки
              курса (или плейсхолдер по умолчанию, если изображение не задано)"
            uk: "*Conditional:* Included when the course preview is loaded. URL прев'ю-картинки
              курсу (або плейсхолдер за замовчуванням, якщо зображення не задано)"
        offers:
          type: array
          items:
            "$ref": "#/components/schemas/OfferIdentifier"
          description: "*Conditional:* Included when offer identity payloads tied
            to the course are loaded. Compact identity entries for offers linked to
            the course"
          x-translations:
            ru: "*Conditional:* Included when offer identity payloads tied to the
              course are loaded. Краткие идентификационные записи предложений, связанных
              с курсом"
            uk: "*Conditional:* Included when offer identity payloads tied to the
              course are loaded. Короткі ідентифікаційні записи пропозицій, пов'язаних
              з курсом"
      required:
      - id
      - product_id
      - type_id
      - title
      - link
    CourseLessonIdentify:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        course:
          allOf:
          - "$ref": "#/components/schemas/CourseIdentify"
          description: "*Conditional:* Included when the parent course identity is
            part of the response."
        module:
          allOf:
          - "$ref": "#/components/schemas/CourseModuleSimple"
          nullable: true
          description: "*Conditional:* Included when the lesson's module branch is
            part of the response. Null for top-level lessons."
      required:
      - id
      - title
    CourseLessonSimple:
      type: object
      properties:
        id:
          type: integer
        course_id:
          type: integer
        type_id:
          type: integer
          description: Lesson type identifier
          x-translations:
            ru: Идентификатор типа урока
            uk: Ідентифікатор типу уроку
        status_id:
          type: integer
          description: Lesson publish status identifier
          x-translations:
            ru: Идентификатор статуса публикации урока
            uk: Ідентифікатор статусу публікації уроку
        number:
          type: string
          description: User-visible lesson number (chapter ordering token)
          x-translations:
            ru: Видимый пользователю номер урока (токен порядка главы)
            uk: Видимий користувачу номер уроку (токен порядку глави)
          nullable: true
        title:
          type: string
        slug:
          type: string
          nullable: true
        link:
          type: string
          description: Public URL to the lesson
          x-translations:
            ru: Публичная ссылка на урок
            uk: Публічне посилання на урок
        quizzes:
          type: array
          items:
            "$ref": "#/components/schemas/QuizIdentifier"
          description: "*Conditional:* Included when lesson quizzes are part of the
            response. Quizzes attached to the lesson"
          x-translations:
            ru: "*Conditional:* Included when lesson quizzes are part of the response.
              Квизы, прикреплённые к уроку"
            uk: "*Conditional:* Included when lesson quizzes are part of the response.
              Квізи, прикріплені до уроку"
        course:
          allOf:
          - "$ref": "#/components/schemas/CourseIdentify"
          description: "*Conditional:* Included when the parent course summary is
            part of the response. Identity payload of the parent course"
          x-translations:
            ru: "*Conditional:* Included when the parent course summary is part of
              the response. Идентификационные данные родительского курса"
            uk: "*Conditional:* Included when the parent course summary is part of
              the response. Ідентифікаційні дані батьківського курсу"
        module:
          allOf:
          - "$ref": "#/components/schemas/CourseModuleSimple"
          description: Parent module summary; null for top-level lessons
          x-translations:
            ru: Сводка по родительскому модулю; null для уроков верхнего уровня
            uk: Зведення батьківського модуля; null для уроків верхнього рівня
          nullable: true
      required:
      - id
      - course_id
      - type_id
      - status_id
      - number
      - title
      - slug
      - link
      - module
    CourseModuleSimple:
      type: object
      properties:
        id:
          type: integer
        course_id:
          type: integer
        number:
          type: string
          description: User-visible module number / ordering token
          x-translations:
            ru: Видимый пользователю номер модуля / токен порядка
            uk: Видимий користувачу номер модуля / токен порядку
          nullable: true
        title:
          type: string
      required:
      - id
      - course_id
      - number
      - title
    CourseProgramSection:
      type: object
      properties:
        id:
          type: integer
          description: course_node-style id of the section. While info-sections are
            not real course_nodes yet, this is `info_sections.id`; after the course-engine
            update it will be the actual `course_node.id`. The contract does not change.
          x-translations:
            ru: course_node-style id секции. Пока info-секции не являются полноценными
              course_node, здесь приходит `info_sections.id`; после апдейта движка
              курсов сюда придёт реальный `course_node.id`. Контракт при этом не изменится.
            uk: course_node-style id секції. Поки info-секції не є повноцінними course_node,
              тут приходить `info_sections.id`; після апдейту рушія курсів сюди прийде
              справжній `course_node.id`. Контракт при цьому не зміниться.
        order:
          type: integer
        course_nodeble_type:
          type: string
          description: Always `info_section` for section nodes. Stable across versions
            — safe to match against.
          x-translations:
            ru: Для секций всегда `info_section`. Значение стабильно между версиями
              — можно матчить напрямую.
            uk: Для секцій завжди `info_section`. Значення стабільне між версіями
              — можна матчити напряму.
        course_nodeble_id:
          type: integer
        course_id:
          type: integer
          nullable: true
        title:
          type: string
          nullable: true
        children:
          type: array
          items:
            type: object
          description: Reserved for the upcoming course-tree update, when info-sections
            become full course-nodes that can contain further children. Currently
            always an empty array.
          x-translations:
            ru: Зарезервировано под предстоящий апдейт дерева курса, когда info-секции
              станут полноценными course-нодами и смогут содержать вложенные дочерние
              узлы. Сейчас всегда пустой массив.
            uk: Зарезервовано під майбутній апдейт дерева курсу, коли info-секції
              стануть повноцінними course-нодами і зможуть містити вкладені дочірні
              вузли. Зараз завжди порожній масив.
        root_id:
          type: integer
          nullable: true
        parent_id:
          type: integer
          description: course_node id of the parent `course_lesson` node this section
            belongs to.
          x-translations:
            ru: course_node id родительского `course_lesson`-узла, которому принадлежит
              секция.
            uk: course_node id батьківського `course_lesson`-вузла, якому належить
              секція.
          nullable: true
        is_public:
          type: boolean
          nullable: true
        link:
          type: string
          nullable: true
        quizzes:
          type: array
          items:
            "$ref": "#/components/schemas/QuizIdentifier"
          description: Quizzes attached to this section as an array of `QuizIdentifier`
            objects (`{id, name}`). Combines quizzes attached via the section's practice
            info-units with lesson-level "List of practice" quizzes mapped onto the
            section that holds them. The pair `(parent_lesson.course_nodeble_id, quizzes[i].id)`
            matches `quiz_progress[].course_lesson_id` + `quiz_id`.
          x-translations:
            ru: Квизы, прикреплённые к секции, в виде массива объектов `QuizIdentifier`
              (`{id, name}`). Объединяет квизы из практик-info-units секции и lesson-level
              квизы из блока "Список практик", которые отображаются в той секции,
              где этот блок размещён. Пара `(parent_lesson.course_nodeble_id, quizzes[i].id)`
              совпадает с `quiz_progress[].course_lesson_id` + `quiz_id`.
            uk: Квізи, прикріплені до секції, у вигляді масиву об'єктів `QuizIdentifier`
              (`{id, name}`). Об'єднує квізи з практик-info-units секції та lesson-level
              квізи з блоку "Список практик", що відображаються у секції, де цей блок
              розміщений. Пара `(parent_lesson.course_nodeble_id, quizzes[i].id)`
              збігається з `quiz_progress[].course_lesson_id` + `quiz_id`.
      required:
      - id
      - order
      - course_nodeble_type
      - course_nodeble_id
      - course_id
      - title
      - children
      - root_id
      - parent_id
      - is_public
      - link
    CourseProgram:
      type: object
      properties:
        id:
          type: integer
        order:
          type: integer
          description: Position among siblings
          x-translations:
            ru: Позиция среди соседних узлов
            uk: Позиція серед сусідніх вузлів
        course_nodeble_type:
          type: string
          description: Polymorphic type of the underlying entity (lesson, module,
            etc.)
          x-translations:
            ru: Полиморфный тип нижележащей сущности (урок, модуль и т.д.)
            uk: Поліморфний тип нижчележачої сутності (урок, модуль тощо)
        course_nodeble_id:
          type: integer
        course_id:
          type: integer
        title:
          type: string
          description: Title of the underlying entity
          x-translations:
            ru: Название нижележащей сущности
            uk: Назва нижчележачої сутності
        preview:
          allOf:
          - "$ref": "#/components/schemas/FileSimple"
          nullable: true
          description: "*Conditional:* Included when the node has a preview image
            attached."
        children:
          type: array
          items:
            "$ref": "#/components/schemas/CourseProgram"
          description: Nested child nodes (empty array for leaves)
          x-translations:
            ru: Вложенные дочерние узлы (пустой массив у листьев)
            uk: Вкладені дочірні вузли (порожній масив у листків)
        root_id:
          type: integer
          description: Identifier of the tree root
          x-translations:
            ru: Идентификатор корня дерева
            uk: Ідентифікатор кореня дерева
          nullable: true
        parent_id:
          type: integer
          nullable: true
        is_public:
          type: boolean
        link:
          type: string
          description: Public link to the node (if applicable)
          x-translations:
            ru: Публичная ссылка на узел (если применимо)
            uk: Публічне посилання на вузол (якщо застосовно)
          nullable: true
      required:
      - id
      - order
      - course_nodeble_type
      - course_nodeble_id
      - course_id
      - title
      - children
      - root_id
      - parent_id
      - is_public
      - link
    CourseProgress:
      type: object
      properties:
        course_id:
          type: integer
        course_url:
          type: string
          description: Public URL to the course landing page
          x-translations:
            ru: Публичная ссылка на лендинг курса
            uk: Публічне посилання на лендінг курсу
        title:
          type: string
          description: Course title
          x-translations:
            ru: Название курса
            uk: Назва курсу
        lessons_count:
          type: integer
          description: Total number of lessons currently active in the course
          x-translations:
            ru: Общее число активных уроков в курсе
            uk: Загальна кількість активних уроків у курсі
        lessons_count_viewed:
          type: integer
          description: Number of lessons the user has opened at least once
          x-translations:
            ru: Число уроков, открытых пользователем хотя бы раз
            uk: Кількість уроків, відкритих користувачем хоча б раз
        lessons_viewed_percentage:
          type: number
          description: Percentage of lessons the user has viewed (0 – 100)
          x-translations:
            ru: Процент просмотренных пользователем уроков (0 – 100)
            uk: Відсоток переглянутих користувачем уроків (0 – 100)
        lessons_count_completed:
          type: integer
          description: Number of lessons the user has finished
          x-translations:
            ru: Число уроков, пройденных пользователем
            uk: Кількість пройдених користувачем уроків
        lessons_completion_percentage:
          type: number
          description: Percentage of lessons the user has finished (0 – 100)
          x-translations:
            ru: Процент пройденных пользователем уроков (0 – 100)
            uk: Відсоток пройдених користувачем уроків (0 – 100)
        lessons_completed_display_percentage:
          type: number
          description: Display-friendly completion percentage (clamped/rounded for
            UI)
          x-translations:
            ru: Процент прохождения для отображения (обрезанный/округлённый для UI)
            uk: Відсоток проходження для відображення (обрізаний/округлений для UI)
        quizzes_count:
          type: integer
          description: Total number of quizzes in the course
          x-translations:
            ru: Общее число квизов в курсе
            uk: Загальна кількість квізів у курсі
        quizzes_count_completed:
          type: integer
          description: Number of quizzes the user has completed
          x-translations:
            ru: Число квизов, пройденных пользователем
            uk: Кількість пройдених користувачем квізів
        quizzes_completion_percentage:
          type: number
          description: Percentage of quizzes the user has completed (0 – 100)
          x-translations:
            ru: Процент пройденных пользователем квизов (0 – 100)
            uk: Відсоток пройдених користувачем квізів (0 – 100)
        scores_max:
          type: number
          description: Maximum total score the user could earn in the course
          x-translations:
            ru: Максимально возможный балл пользователя по курсу
            uk: Максимально можливий бал користувача за курс
        quizzes_scores:
          type: number
          description: Total score earned by the user across all quizzes
          x-translations:
            ru: Суммарный балл пользователя по всем квизам
            uk: Сумарний бал користувача за всіма квізами
        product_scores:
          type: number
          description: Total product-level score earned by the user (outside quizzes)
          x-translations:
            ru: Суммарный балл пользователя на уровне продукта (вне квизов)
            uk: Сумарний бал користувача на рівні продукту (поза квізами)
        scores:
          type: number
          description: Combined score the user has earned across the course
          x-translations:
            ru: Суммарный балл пользователя по всему курсу
            uk: Сумарний бал користувача за всім курсом
        is_completed:
          type: boolean
          description: True when the user has fully completed the course
          x-translations:
            ru: True, если пользователь полностью прошёл курс
            uk: True, якщо користувач повністю пройшов курс
        completed_at:
          type: string
          format: date-time
          description: Timestamp of full course completion; null while in progress
          x-translations:
            ru: Момент полного завершения курса; null пока курс в процессе
            uk: Момент повного завершення курсу; null поки курс у процесі
          nullable: true
        current_lesson:
          allOf:
          - "$ref": "#/components/schemas/CourseLessonSimple"
          description: Lesson the user is currently positioned on (null if none)
          x-translations:
            ru: Урок, на котором пользователь сейчас находится (null, если нет)
            uk: Урок, на якому користувач зараз знаходиться (null, якщо немає)
          nullable: true
        next_lesson:
          allOf:
          - "$ref": "#/components/schemas/CourseLessonSimple"
          description: Next lesson available to the user (null when at the end)
          x-translations:
            ru: Следующий доступный пользователю урок (null в конце курса)
            uk: Наступний доступний користувачу урок (null у кінці курсу)
          nullable: true
        last_active_at:
          type: string
          format: date-time
          description: Timestamp of the user's most recent activity in the course
          x-translations:
            ru: Момент последней активности пользователя в курсе
            uk: Момент останньої активності користувача в курсі
          nullable: true
        is_checkpoints_skipped:
          type: boolean
          description: True when the user has skipped through gating checkpoints
          x-translations:
            ru: True, если пользователь пропустил блокирующие чекпоинты
            uk: True, якщо користувач пропустив блокувальні чекпоінти
        checkpoints:
          type: array
          items:
            "$ref": "#/components/schemas/QuizIdentifier"
          description: Checkpoint quizzes the user has reached
          x-translations:
            ru: Чекпоинт-квизы, до которых дошёл пользователь
            uk: Чекпоінт-квізи, до яких дійшов користувач
        is_current_dripping_date_skipped:
          type: boolean
          description: True when the user bypassed the current drip-content gate
          x-translations:
            ru: True, если пользователь обошёл текущий drip-content барьер
            uk: True, якщо користувач обійшов поточний drip-content бар'єр
        is_next_dripping_date_skipped:
          type: boolean
          description: True when the user bypassed the upcoming drip-content gate
          x-translations:
            ru: True, если пользователь обошёл следующий drip-content барьер
            uk: True, якщо користувач обійшов наступний drip-content бар'єр
        current_dripping_date:
          type: string
          format: date-time
          description: When the current drip-content gate unlocks
          x-translations:
            ru: Когда разблокируется текущий drip-content барьер
            uk: Коли розблоковується поточний drip-content бар'єр
          nullable: true
        next_dripping_date:
          type: string
          format: date-time
          description: When the next drip-content gate unlocks
          x-translations:
            ru: Когда разблокируется следующий drip-content барьер
            uk: Коли розблоковується наступний drip-content бар'єр
          nullable: true
      required:
      - course_id
      - course_url
      - title
      - lessons_count
      - lessons_count_viewed
      - lessons_viewed_percentage
      - lessons_count_completed
      - lessons_completion_percentage
      - lessons_completed_display_percentage
      - quizzes_count
      - quizzes_count_completed
      - quizzes_completion_percentage
      - scores_max
      - quizzes_scores
      - product_scores
      - scores
      - is_completed
      - completed_at
      - current_lesson
      - next_lesson
      - last_active_at
      - is_checkpoints_skipped
      - checkpoints
      - is_current_dripping_date_skipped
      - is_next_dripping_date_skipped
      - current_dripping_date
      - next_dripping_date
    CourseUser:
      type: object
      properties:
        user:
          allOf:
          - "$ref": "#/components/schemas/UserSimple"
          description: The student account participating in the course — identifier,
            name and email. Stable across requests.
          x-translations:
            ru: Аккаунт ученика, который участвует в курсе — идентификатор, имя, email.
              Стабильно между запросами.
            uk: Акаунт учня, який бере участь у курсі — ідентифікатор, ім'я, email.
              Стабільно між запитами.
        contact:
          allOf:
          - "$ref": "#/components/schemas/Contact"
          description: Linked CRM contact record for this student.
          x-translations:
            ru: Связанная запись CRM-контакта для этого ученика.
            uk: Пов'язаний запис CRM-контакту для цього учня.
        course_points:
          type: integer
          description: Total points the user has earned in the course
          x-translations:
            ru: Общее количество баллов пользователя в курсе
            uk: Загальна кількість балів користувача в курсі
        course_progress:
          allOf:
          - "$ref": "#/components/schemas/CourseProgress"
          description: Lesson/module completion summary
          x-translations:
            ru: Сводка по прохождению уроков/модулей
            uk: Зведення проходження уроків/модулів
        lessons_available_count:
          type: integer
          description: Number of lessons the user currently has access to
          x-translations:
            ru: Количество уроков, доступных пользователю сейчас
            uk: Кількість уроків, доступних користувачу зараз
        is_full_access:
          type: boolean
          description: True when the user has access to every active lesson in the
            course
          x-translations:
            ru: True, если у пользователя есть доступ ко всем активным урокам курса
            uk: True, якщо у користувача є доступ до всіх активних уроків курсу
        subscription:
          allOf:
          - "$ref": "#/components/schemas/ProductAggregatedSubscription"
          description: "*Conditional:* Included when the user has an aggregated subscription
            for the course."
        lesson_progress:
          type: array
          items:
            "$ref": "#/components/schemas/LessonProgress"
          description: "*Conditional:* Included when <code>include</code> contains
            <code>lesson_progress</code>. Array of `LessonProgress` objects — per-lesson
            watched/completed snapshots for this user. Returned when `include` contains
            `lesson_progress`."
          x-translations:
            ru: "*Conditional:* Included when <code>include</code> contains <code>lesson_progress</code>.
              Массив объектов `LessonProgress` — per-lesson снимки просмотра/завершения
              для этого пользователя. Возвращается при `include=lesson_progress`."
            uk: "*Conditional:* Included when <code>include</code> contains <code>lesson_progress</code>.
              Масив об'єктів `LessonProgress` — per-lesson знімки перегляду/завершення
              для цього користувача. Повертається при `include=lesson_progress`."
        module_progress:
          type: array
          items:
            "$ref": "#/components/schemas/ModuleProgress"
          description: "*Conditional:* Included when <code>include</code> contains
            <code>module_progress</code>. Array of `ModuleProgress` objects — per-module
            watched/completed snapshots for this user (aggregated server-side from
            the module's lessons). Returned when `include` contains `module_progress`."
          x-translations:
            ru: "*Conditional:* Included when <code>include</code> contains <code>module_progress</code>.
              Массив объектов `ModuleProgress` — per-module снимки просмотра/завершения
              для этого пользователя (агрегируются сервером из уроков модуля). Возвращается
              при `include=module_progress`."
            uk: "*Conditional:* Included when <code>include</code> contains <code>module_progress</code>.
              Масив об'єктів `ModuleProgress` — per-module знімки перегляду/завершення
              для цього користувача (агрегуються сервером з уроків модуля). Повертається
              при `include=module_progress`."
        quiz_progress:
          type: array
          items:
            "$ref": "#/components/schemas/QuizProgress"
          description: "*Conditional:* Included when <code>include</code> contains
            <code>quiz_progress</code>. Array of `QuizProgress` objects — one entry
            per `(course_lesson_id, quiz_id)` pair summarised from the user's latest
            non-cancelled attempt. Returned when `include` contains `quiz_progress`."
          x-translations:
            ru: "*Conditional:* Included when <code>include</code> contains <code>quiz_progress</code>.
              Массив объектов `QuizProgress` — по одной записи на пару `(course_lesson_id,
              quiz_id)` со сводкой по последней non-cancelled попытке ученика. Возвращается
              при `include=quiz_progress`."
            uk: "*Conditional:* Included when <code>include</code> contains <code>quiz_progress</code>.
              Масив об'єктів `QuizProgress` — по одному запису на пару `(course_lesson_id,
              quiz_id)` зі зведенням за останньою non-cancelled спробою учня. Повертається
              при `include=quiz_progress`."
      required:
      - user
      - contact
      - course_points
      - course_progress
      - lessons_available_count
      - is_full_access
    Course:
      type: object
      properties:
        id:
          type: integer
        product_id:
          type: integer
          description: Linked product identifier (use for /contacts/{contact}/products
            lookup)
          x-translations:
            ru: Идентификатор связанного продукта (для поиска через /contacts/{contact}/products)
            uk: Ідентифікатор пов'язаного продукту (для пошуку через /contacts/{contact}/products)
        type:
          type: string
          description: Course type identifier (course / marathon / etc.)
          x-translations:
            ru: Идентификатор типа курса (course / marathon / и т.д.)
            uk: Ідентифікатор типу курсу (course / marathon / тощо)
          example: course
        title:
          type: string
          description: Course title (falls back to `Course
          x-translations:
            ru: Название курса (фолбэк на `Course
            uk: Назва курсу (фолбек на `Course
        slug:
          type: string
          description: URL-friendly slug
          x-translations:
            ru: URL-совместимый slug
            uk: URL-сумісний slug
          nullable: true
        preview:
          allOf:
          - "$ref": "#/components/schemas/FileSimple"
          description: "*Conditional:* Included when the course has a preview image."
        link:
          type: string
          description: Public link to the course landing page
          x-translations:
            ru: Публичная ссылка на лендинг курса
            uk: Публічне посилання на лендінг курсу
        status:
          type: string
          description: Course publish status identifier (`draft`, `published`, etc.)
          x-translations:
            ru: Идентификатор статуса публикации курса (`draft`, `published` и т.д.)
            uk: Ідентифікатор статусу публікації курсу (`draft`, `published` тощо)
        offers:
          type: array
          items:
            "$ref": "#/components/schemas/Offer"
          description: "*Conditional:* Included when the course offers are part of
            the response."
        info_units:
          type: array
          items:
            "$ref": "#/components/schemas/InfoUnit"
          description: "*Conditional:* Included when course info units are part of
            the response."
        course_program:
          type: array
          items:
            "$ref": "#/components/schemas/CourseProgram"
          description: "*Conditional:* Included when the course program is part of
            the response (rendered as a tree)."
      required:
      - id
      - product_id
      - type
      - title
      - slug
      - link
      - status
    InfoUnit:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description_formatted:
          type: string
          description: Rich-text description with links processed for display
          x-translations:
            ru: Описание с rich-text форматированием и обработанными ссылками
            uk: Опис з rich-text форматуванням та обробленими посиланнями
          nullable: true
        has_external_links:
          type: boolean
          description: True if the description contains links pointing outside the
            cabinet
          x-translations:
            ru: True, если описание содержит внешние ссылки
            uk: True, якщо опис містить зовнішні посилання
        position:
          type: integer
          description: Position of the unit within its parent collection
          x-translations:
            ru: Позиция элемента в родительской коллекции
            uk: Позиція елемента в батьківській колекції
        type_id:
          type: integer
          description: Info-unit type identifier
          x-translations:
            ru: Идентификатор типа info-unit
            uk: Ідентифікатор типу info-unit
      required:
      - id
      - title
      - description_formatted
      - has_external_links
      - position
      - type_id
    LessonProgress:
      type: object
      properties:
        lesson_id:
          type: integer
        is_watched:
          type: boolean
        is_completed:
          type: boolean
        quizzing_status:
          allOf:
          - "$ref": "#/components/schemas/EnumValue"
          description: |
            Quiz state for this lesson (enum). Possible cases (`id` / `slug`):
            `1` / `WithoutQuizzes`, `2` / `NotStarted`, `3` / `InProgress`,
            `4` / `Completed`, `5` / `NotDetected`.
          x-translations:
            ru: |
              Статус квизов урока (enum). Возможные кейсы (`id` / `slug`):
              `1` / `WithoutQuizzes`, `2` / `NotStarted`, `3` / `InProgress`,
              `4` / `Completed`, `5` / `NotDetected`.
            uk: |
              Статус квізів уроку (enum). Можливі кейси (`id` / `slug`):
              `1` / `WithoutQuizzes`, `2` / `NotStarted`, `3` / `InProgress`,
              `4` / `Completed`, `5` / `NotDetected`.
          nullable: true
        watching_start_at:
          type: string
          format: date-time
          nullable: true
        watching_end_at:
          type: string
          format: date-time
          nullable: true
        quizzing_start_at:
          type: string
          format: date-time
          nullable: true
        quizzing_end_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
      required:
      - lesson_id
      - is_watched
      - is_completed
    ModuleProgress:
      type: object
      properties:
        module_id:
          type: integer
        is_watched:
          type: boolean
        is_completed:
          type: boolean
        quizzing_status:
          allOf:
          - "$ref": "#/components/schemas/EnumValue"
          description: |
            Aggregated quiz state for this module (enum). Same cases as
            `lesson_progress.quizzing_status`: `1` / `WithoutQuizzes`,
            `2` / `NotStarted`, `3` / `InProgress`, `4` / `Completed`,
            `5` / `NotDetected`.
          x-translations:
            ru: |
              Агрегированный статус квизов модуля (enum). Те же кейсы, что у
              `lesson_progress.quizzing_status`: `1` / `WithoutQuizzes`,
              `2` / `NotStarted`, `3` / `InProgress`, `4` / `Completed`,
              `5` / `NotDetected`.
            uk: |
              Агрегований статус квізів модуля (enum). Ті самі кейси, що в
              `lesson_progress.quizzing_status`: `1` / `WithoutQuizzes`,
              `2` / `NotStarted`, `3` / `InProgress`, `4` / `Completed`,
              `5` / `NotDetected`.
          nullable: true
        watching_start_at:
          type: string
          format: date-time
          nullable: true
        watching_end_at:
          type: string
          format: date-time
          nullable: true
        quizzing_start_at:
          type: string
          format: date-time
          nullable: true
        quizzing_end_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
          nullable: true
      required:
      - module_id
      - is_watched
      - is_completed
    QuizAttemptIdentifier:
      type: object
      properties:
        id:
          type: integer
        course_id:
          type: integer
          nullable: true
        quiz_id:
          type: integer
        user_id:
          type: integer
        status_id:
          type: integer
          description: Attempt status identifier (passed / failed / in-progress)
          x-translations:
            ru: Идентификатор статуса попытки (passed / failed / in-progress)
            uk: Ідентифікатор статусу спроби (passed / failed / in-progress)
        comment:
          type: string
          nullable: true
        quiz:
          type: object
          description: "*Conditional:* Included when the quiz relation is loaded.
            Quiz metadata payload"
          x-translations:
            ru: "*Conditional:* Included when the quiz relation is loaded. Метаданные
              квиза"
            uk: "*Conditional:* Included when the quiz relation is loaded. Метадані
              квіза"
        course:
          allOf:
          - "$ref": "#/components/schemas/Course"
          description: "*Conditional:* Included when the parent course is loaded."
        lesson:
          type: object
          description: "*Conditional:* Included when the parent lesson is loaded.
            Lesson metadata payload"
          x-translations:
            ru: "*Conditional:* Included when the parent lesson is loaded. Метаданные
              урока"
            uk: "*Conditional:* Included when the parent lesson is loaded. Метадані
              уроку"
      required:
      - id
      - course_id
      - quiz_id
      - user_id
      - status_id
      - comment
    QuizIdentifier:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
          description: Quiz name
          x-translations:
            ru: Название квиза
            uk: Назва квіза
      required:
      - id
      - name
    QuizProgress:
      type: object
      properties:
        course_lesson_id:
          type: integer
        quiz_id:
          type: integer
        status:
          allOf:
          - "$ref": "#/components/schemas/EnumValue"
          description: |
            Latest attempt status (enum). Possible cases (`id` / `slug`):
            `1` / `Passed`, `2` / `Failed`, `3` / `NeedRework`,
            `4` / `LastAttemptChangedStatus`, `5` / `InProcess`,
            `6` / `PendingCheck`, `7` / `DoesntStart`.
          x-translations:
            ru: |
              Статус последней попытки (enum). Возможные кейсы (`id` / `slug`):
              `1` / `Passed`, `2` / `Failed`, `3` / `NeedRework`,
              `4` / `LastAttemptChangedStatus`, `5` / `InProcess`,
              `6` / `PendingCheck`, `7` / `DoesntStart`.
            uk: |
              Статус останньої спроби (enum). Можливі кейси (`id` / `slug`):
              `1` / `Passed`, `2` / `Failed`, `3` / `NeedRework`,
              `4` / `LastAttemptChangedStatus`, `5` / `InProcess`,
              `6` / `PendingCheck`, `7` / `DoesntStart`.
          nullable: true
        scores:
          type: number
          nullable: true
        scores_max:
          type: number
          description: Maximum possible score, taken from the current quiz configuration
            (not from the attempt record).
          x-translations:
            ru: Максимально возможный балл, берётся из текущей конфигурации квиза
              (а не из записи попытки).
            uk: Максимально можливий бал, береться з поточної конфігурації квіза (а
              не з запису спроби).
          nullable: true
        started_at:
          type: string
          format: date-time
          description: When this attempt was started.
          x-translations:
            ru: Когда попытка была начата.
            uk: Коли спробу було розпочато.
          nullable: true
        finished_at:
          type: string
          format: date-time
          description: When this attempt was finished. `null` while the attempt is
            still in progress.
          x-translations:
            ru: Когда попытка была завершена. `null`, если попытка ещё в процессе.
            uk: Коли спробу було завершено. `null`, якщо спроба ще у процесі.
          nullable: true
        checked_at:
          type: string
          format: date-time
          description: When this attempt was manually reviewed. `null` for quizzes
            that don't require review or for attempts not yet checked.
          x-translations:
            ru: Когда попытка была проверена вручную. `null` для квизов без ручной
              проверки или для непроверенных попыток.
            uk: Коли спробу було перевірено вручну. `null` для квізів без ручної перевірки
              або для неперевірених спроб.
          nullable: true
        last_activity_at:
          type: string
          format: date-time
          description: Last time the user interacted with this attempt (answer change,
            navigation, etc.). Useful to detect stale in-progress attempts.
          x-translations:
            ru: Последняя активность ученика в попытке (смена ответа, переход и т.п.).
              Удобно, чтобы понимать «зависшие» in-progress попытки.
            uk: Остання активність учня у спробі (зміна відповіді, перехід тощо).
              Зручно для виявлення «завислих» in-progress спроб.
          nullable: true
        attempt_number:
          type: integer
          description: 1-based sequential number of the returned attempt among the
            user's non-cancelled attempts on this `(course_lesson_id, quiz_id)` pair.
            Since the endpoint always returns the latest non-cancelled attempt, this
            is equivalent to the total count of non-cancelled attempts — useful when
            you want to know how many times the user has tried this quiz.
          x-translations:
            ru: Порядковый номер возвращённой попытки (от 1) среди non-cancelled попыток
              ученика по этой паре `(course_lesson_id, quiz_id)`. Поскольку эндпоинт
              всегда возвращает последнюю non-cancelled попытку, это значение равно
              общему количеству non-cancelled попыток — удобно, когда нужно понимать,
              сколько раз ученик пытался пройти квиз.
            uk: Порядковий номер повернутої спроби (від 1) серед non-cancelled спроб
              учня по цій парі `(course_lesson_id, quiz_id)`. Оскільки ендпоінт завжди
              повертає останню non-cancelled спробу, це значення дорівнює загальній
              кількості non-cancelled спроб — корисно, коли потрібно знати, скільки
              разів учень намагався пройти квіз.
      required:
      - course_lesson_id
      - quiz_id
      - attempt_number
    EmailCampaignIdentifier:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Campaign title; falls back to a localized label for system
            campaigns
          x-translations:
            ru: Название рассылки; для системных рассылок фолбэк на локализованную
              подпись
            uk: Назва розсилки; для системних розсилок фолбек на локалізовану підпис
      required:
      - id
      - title
    EmailCampaign:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Campaign title; falls back to a localized "Transaction" label
            for system campaigns
          x-translations:
            ru: Название рассылки; для системных рассылок фолбэк на локализованную
              подпись «Транзакционная»
            uk: Назва розсилки; для системних розсилок фолбек на локалізовану підпис
              «Транзакційна»
        state_id:
          type: integer
          description: Campaign state identifier
          x-translations:
            ru: Идентификатор состояния рассылки
            uk: Ідентифікатор стану розсилки
        is_restricted:
          type: boolean
          description: True for built-in system campaigns that can't be edited
          x-translations:
            ru: True для встроенных системных рассылок, которые нельзя редактировать
            uk: True для вбудованих системних розсилок, які не можна редагувати
        not_verified:
          type: boolean
          description: True when the owning account lacks a verified payment method
            / replenishment
          x-translations:
            ru: True, если у владельца аккаунта нет подтверждённого способа оплаты
              / пополнения
            uk: True, якщо у власника акаунту немає підтвердженого способу оплати
              / поповнення
        is_transactional:
          type: boolean
        is_system:
          type: boolean
        is_segment:
          type: boolean
          description: True when the campaign targets a dynamic segment
          x-translations:
            ru: True, если рассылка нацелена на динамический сегмент
            uk: True, якщо розсилка націлена на динамічний сегмент
        cabinet_id:
          type: integer
        user_id:
          type: integer
        subject:
          type: string
          nullable: true
        subsubject:
          type: string
          description: Pre-header / preview text shown beneath the subject
          x-translations:
            ru: Прехедер / превью-текст, отображаемый под темой письма
            uk: Прехедер / прев'ю-текст, що відображається під темою листа
          nullable: true
        start_type:
          type: string
          description: Schedule type identifier (immediate, scheduled, recurring,
            etc.)
          x-translations:
            ru: Идентификатор типа расписания (immediate, scheduled, recurring и т.п.)
            uk: Ідентифікатор типу розкладу (immediate, scheduled, recurring тощо)
          nullable: true
        start_at_utc:
          type: string
          format: date-time
          nullable: true
        start_at:
          type: string
          format: date-time
          nullable: true
        timezone_id:
          type: integer
          nullable: true
        paused_at:
          type: string
          format: date-time
          nullable: true
        rejected_at:
          type: string
          format: date-time
          nullable: true
        finished_at:
          type: string
          format: date-time
          nullable: true
        timezone:
          allOf:
          - "$ref": "#/components/schemas/Timezone"
          nullable: true
        contact_lists:
          type: array
          items:
            "$ref": "#/components/schemas/ContactList"
          description: "*Conditional:* Included when the campaign's contact lists
            are loaded."
        bounced_limit:
          type: number
          description: Threshold above which the bounce rate is considered problematic
          x-translations:
            ru: Порог, выше которого доля bounce считается проблемной
            uk: Поріг, вище якого частка bounce вважається проблемною
        complaint_limit:
          type: number
          description: Threshold above which the complaint rate is considered problematic
          x-translations:
            ru: Порог, выше которого доля жалоб считается проблемной
            uk: Поріг, вище якого частка скарг вважається проблемною
        count_recipients:
          type: integer
          description: "*Conditional:* Included when the campaign statistic is loaded."
        complaint_rate:
          type: number
          description: "*Conditional:* Included when the campaign statistic is loaded."
        bounced_rate:
          type: number
          description: "*Conditional:* Included when the campaign statistic is loaded."
        count_opened:
          type: integer
          description: "*Conditional:* Included when the campaign statistic is loaded."
        count_clicks:
          type: integer
          description: "*Conditional:* Included when the campaign statistic is loaded."
        percent_opened:
          type: number
          description: "*Conditional:* Included when the campaign statistic is loaded."
        percent_clicks:
          type: number
          description: "*Conditional:* Included when the campaign statistic is loaded."
        percent_completed:
          type: number
          description: "*Conditional:* Included when the campaign statistic is loaded."
        distributor_id:
          type: integer
          nullable: true
          description: "*Conditional:* Returned only for viewers with extended mailing
            access."
        templatable_type:
          type: string
          nullable: true
          description: "*Conditional:* Returned only for viewers with extended mailing
            access."
        templatable_id:
          type: integer
          nullable: true
          description: "*Conditional:* Returned only for viewers with extended mailing
            access."
        template:
          type: object
          description: "*Conditional:* Returned only for viewers with extended mailing
            access when the templatable relation is loaded. Email template payload
            (system or custom)"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended mailing access
              when the templatable relation is loaded. Данные email-шаблона (системный
              или кастомный)"
            uk: "*Conditional:* Returned only for viewers with extended mailing access
              when the templatable relation is loaded. Дані email-шаблону (системний
              або кастомний)"
        distributor:
          type: object
          description: "*Conditional:* Returned only for viewers with extended mailing
            access when the distributor relation is loaded."
        statistic:
          type: object
          description: "*Conditional:* Returned only for viewers with extended mailing
            access when the statistic relation is loaded. Full campaign statistic
            snapshot"
          x-translations:
            ru: "*Conditional:* Returned only for viewers with extended mailing access
              when the statistic relation is loaded. Полный снимок статистики рассылки"
            uk: "*Conditional:* Returned only for viewers with extended mailing access
              when the statistic relation is loaded. Повний знімок статистики розсилки"
      required:
      - id
      - title
      - state_id
      - is_restricted
      - not_verified
      - is_transactional
      - is_system
      - is_segment
      - cabinet_id
      - user_id
      - subject
      - subsubject
      - start_type
      - start_at_utc
      - start_at
      - timezone_id
      - paused_at
      - rejected_at
      - finished_at
      - timezone
      - bounced_limit
      - complaint_limit
    AffectedSubscriptions:
      type: object
      properties:
        affected_subscriptions:
          type: array
          items:
            type: object
          description: IDs of subscription records that were deleted
          x-translations:
            ru: ID удалённых записей подписок
            uk: ID видалених записів підписок
        affected_orders:
          type: array
          items:
            type: object
          description: IDs of orders whose access was affected (became custom)
          x-translations:
            ru: ID заказов, у которых был затронут доступ (стали custom)
            uk: ID замовлень, у яких було порушено доступ (стали custom)
        affected_by_offers:
          type: object
          description: Per-offer breakdown keyed by offer_id; each value contains
            `affected_subscriptions` and `affected_orders` integer arrays. Empty object
            when no offers were passed in the request.
          x-translations:
            ru: Разбивка по offer_id; каждое значение содержит массивы `affected_subscriptions`
              и `affected_orders`. Пустой объект, если offers не передавались.
            uk: Розбивка за offer_id; кожне значення містить масиви `affected_subscriptions`
              і `affected_orders`. Порожній обʼєкт, якщо offers не передавалися.
        affected_by_products:
          type: object
          description: Per-product breakdown keyed by product_id; same shape as `affected_by_offers`.
            Empty object when no product_id was passed.
          x-translations:
            ru: Разбивка по product_id; та же структура, что у `affected_by_offers`.
              Пустой объект, если product_id не передавался.
            uk: Розбивка за product_id; та сама структура, що в `affected_by_offers`.
              Порожній обʼєкт, якщо product_id не передавався.
      required:
      - affected_subscriptions
      - affected_orders
      - affected_by_offers
      - affected_by_products
    PaginationLinks:
      type: object
      properties:
        first:
          type: string
          description: URL of the first page
          x-translations:
            ru: URL первой страницы
            uk: URL першої сторінки
          nullable: true
          example: https://api.kwiga.com/api/v1/contacts?page=1
        last:
          type: string
          description: URL of the last page
          x-translations:
            ru: URL последней страницы
            uk: URL останньої сторінки
          nullable: true
          example: https://api.kwiga.com/api/v1/contacts?page=10
        prev:
          type: string
          description: URL of the previous page (null when on the first page)
          x-translations:
            ru: URL предыдущей страницы (null на первой)
            uk: URL попередньої сторінки (null на першій)
          nullable: true
        next:
          type: string
          description: URL of the next page (null when on the last page)
          x-translations:
            ru: URL следующей страницы (null на последней)
            uk: URL наступної сторінки (null на останній)
          nullable: true
          example: https://api.kwiga.com/api/v1/contacts?page=2
      required:
      - first
      - last
      - prev
      - next
    PaginationMetaLink:
      type: object
      properties:
        url:
          type: string
          description: Link target URL (null for the active page)
          x-translations:
            ru: URL ссылки (null для текущей страницы)
            uk: URL посилання (null для поточної сторінки)
          nullable: true
        label:
          type: string
          description: Display label — page number, "&laquo; Previous", "Next &raquo;",
            etc.
          x-translations:
            ru: Отображаемая подпись — номер страницы, "&laquo; Previous", "Next &raquo;"
              и т.п.
            uk: Відображуваний підпис — номер сторінки, "&laquo; Previous", "Next
              &raquo;" тощо
        active:
          type: boolean
          description: True for the entry representing the current page
          x-translations:
            ru: True для элемента, представляющего текущую страницу
            uk: True для елемента, що представляє поточну сторінку
      required:
      - url
      - label
      - active
    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
          example: 1
        from:
          type: integer
          description: Index of the first item on the current page (1-based)
          x-translations:
            ru: Индекс первого элемента на текущей странице (с 1)
            uk: Індекс першого елемента на поточній сторінці (з 1)
          nullable: true
          example: 1
        last_page:
          type: integer
          example: 10
        links:
          type: array
          items:
            "$ref": "#/components/schemas/PaginationMetaLink"
          description: Navigation items (prev / page-number buttons / next) for rendering
            a pager UI
          x-translations:
            ru: Элементы навигации (prev / кнопки-номера страниц / next) для отрисовки
              пагинатора
            uk: Елементи навігації (prev / кнопки-номери сторінок / next) для відмалювання
              пагінатора
        path:
          type: string
          description: Base path used to build pagination URLs
          x-translations:
            ru: Базовый путь для построения URL пагинации
            uk: Базовий шлях для побудови URL пагінації
          example: https://api.kwiga.com/api/v1/contacts
        per_page:
          type: integer
          example: 15
        to:
          type: integer
          description: Index of the last item on the current page (1-based)
          x-translations:
            ru: Индекс последнего элемента на текущей странице (с 1)
            uk: Індекс останнього елемента на поточній сторінці (з 1)
          nullable: true
          example: 15
        total:
          type: integer
          description: Total number of items across all pages
          x-translations:
            ru: Общее количество элементов по всем страницам
            uk: Загальна кількість елементів за всіма сторінками
          example: 142
      required:
      - current_page
      - from
      - last_page
      - links
      - path
      - per_page
      - to
      - total
    Success:
      type: object
      properties:
        success:
          type: boolean
          example: true
      required:
      - success
    OfferDiscount:
      type: object
      properties:
        id:
          type: integer
        price_discounted:
          type: number
          description: Final price after the discount is applied
          x-translations:
            ru: Финальная цена с применённой скидкой
            uk: Фінальна ціна із застосованою знижкою
        limit_type:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Discount limit type enum payload
          x-translations:
            ru: Тип ограничения скидки (enum-payload)
            uk: Тип обмеження знижки (enum-payload)
        allow_payment_in_installments:
          type: boolean
        start_at:
          type: string
          format: date-time
          nullable: true
        start_at_utc:
          type: string
          format: date-time
          nullable: true
        start_timezone_id:
          type: integer
          nullable: true
        end_at:
          type: string
          format: date-time
          nullable: true
        end_type:
          type: string
          description: How the discount ends (fixed date, after sales count, etc.)
          x-translations:
            ru: Как заканчивается скидка (фиксированная дата, после числа продаж и
              т.п.)
            uk: Як закінчується знижка (фіксована дата, після числа продажів тощо)
          nullable: true
        end_at_utc:
          type: string
          format: date-time
          nullable: true
        end_timezone_id:
          type: integer
          nullable: true
        sales_limit:
          type: integer
          description: Maximum number of discounted sales allowed
          x-translations:
            ru: Максимальное число продаж со скидкой
            uk: Максимальна кількість продажів зі знижкою
          nullable: true
        sales:
          type: integer
          description: Number of discounted sales already made
          x-translations:
            ru: Сколько продаж со скидкой уже произошло
            uk: Скільки продажів зі знижкою вже відбулося
        is_active:
          type: boolean
        is_available:
          type: boolean
          description: Whether the discount is currently valid (not expired, not exhausted)
          x-translations:
            ru: Действует ли скидка сейчас (не истекла, не исчерпана)
            uk: Чи діє знижка зараз (не закінчилась, не вичерпана)
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        offer:
          allOf:
          - "$ref": "#/components/schemas/OfferIdentifier"
          description: "*Conditional:* Included when the parent offer is part of the
            response. Compact identity payload of the parent offer"
          x-translations:
            ru: "*Conditional:* Included when the parent offer is part of the response.
              Краткие идентификационные данные родительского предложения"
            uk: "*Conditional:* Included when the parent offer is part of the response.
              Короткі ідентифікаційні дані батьківської пропозиції"
        start_timezone:
          allOf:
          - "$ref": "#/components/schemas/Timezone"
          nullable: true
        end_timezone:
          allOf:
          - "$ref": "#/components/schemas/Timezone"
          nullable: true
      required:
      - id
      - price_discounted
      - limit_type
      - allow_payment_in_installments
      - start_at
      - start_at_utc
      - start_timezone_id
      - end_at
      - end_type
      - end_at_utc
      - end_timezone_id
      - sales_limit
      - sales
      - is_active
      - is_available
      - created_at
      - updated_at
      - start_timezone
      - end_timezone
    OfferIdentifier:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
      required:
      - id
      - title
    OfferSimple:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
        description:
          type: string
          nullable: true
        url:
          type: string
          description: Public purchase URL for the offer
          x-translations:
            ru: Публичный URL для покупки предложения
            uk: Публічний URL для купівлі пропозиції
        type_id:
          type: integer
          description: Offer type identifier
          x-translations:
            ru: Идентификатор типа предложения
            uk: Ідентифікатор типу пропозиції
        status:
          type: string
          description: Offer status identifier
          x-translations:
            ru: Идентификатор статуса предложения
            uk: Ідентифікатор статусу пропозиції
          nullable: true
        subscription_type_payment_by_merchant:
          type: boolean
          description: Whether the subscription is billed by the merchant
          x-translations:
            ru: Тарифицирует ли подписку мерчант
            uk: Чи тарифікує підписку мерчант
          nullable: true
        products:
          type: array
          items:
            "$ref": "#/components/schemas/ProductIdentifier"
          description: "*Conditional:* Included when product identity entries are
            loaded for the offer."
        curators:
          type: array
          items:
            type: object
          description: "*Conditional:* Included when curator entries are loaded for
            the offer."
        is_paid:
          type: boolean
          description: "*Conditional:* Included when payment status is determined
            for the current viewer."
      required:
      - id
      - title
      - description
      - url
      - type_id
      - status
      - subscription_type_payment_by_merchant
    Offer:
      type: object
      properties:
        id:
          type: integer
        unique_offer_code:
          type: string
          description: Stable code used in payment URLs
          x-translations:
            ru: Стабильный код, используемый в URL оплаты
            uk: Стабільний код, що використовується в URL оплати
          nullable: true
        url:
          type: string
          description: Public purchase URL for the offer
          x-translations:
            ru: Публичный URL для покупки предложения
            uk: Публічний URL для купівлі пропозиції
        title:
          type: string
          example: Annual subscription
        description:
          type: string
          nullable: true
        short_description:
          type: string
          nullable: true
        price_type:
          type: string
          description: Offer pricing model identifier (e.g. `free`, `fixed`, `recurring`)
          x-translations:
            ru: Идентификатор модели цены предложения (`free`, `fixed`, `recurring`)
            uk: Ідентифікатор моделі ціни пропозиції (`free`, `fixed`, `recurring`)
        price:
          "$ref": "#/components/schemas/Price"
        price_discounted:
          allOf:
          - "$ref": "#/components/schemas/Price"
          description: "*Conditional:* Included when the offer has an active discount.
            Price after the active discount is applied"
          x-translations:
            ru: "*Conditional:* Included when the offer has an active discount. Цена
              с применённой активной скидкой"
            uk: "*Conditional:* Included when the offer has an active discount. Ціна
              із застосованою активною знижкою"
        discount:
          allOf:
          - "$ref": "#/components/schemas/OfferDiscount"
          nullable: true
          description: "*Conditional:* Included when the offer has an active, currently
            valid discount."
        has_subscription:
          type: boolean
        limit_type:
          type: string
          description: Sales limit type identifier (e.g. `unlimited`, `limited`)
          x-translations:
            ru: Идентификатор типа ограничения продаж (`unlimited`, `limited`)
            uk: Ідентифікатор типу обмеження продажів (`unlimited`, `limited`)
          nullable: true
        limit_of_sales:
          type: integer
          description: Maximum number of purchases allowed (null when unlimited)
          x-translations:
            ru: Максимум разрешённых покупок (null если без лимита)
            uk: Максимум дозволених покупок (null якщо без ліміту)
          nullable: true
        purchases_count:
          type: integer
          description: "*Conditional:* Included when purchase counts are available
            for the offer."
        sales_left:
          type: integer
          nullable: true
          description: "*Conditional:* Included when purchase counts are available.
            Null when the offer has no sales limit."
        is_draft:
          type: boolean
        is_active:
          type: boolean
          description: "*Conditional:* Included when the offer has date settings configured."
        validity_start:
          allOf:
          - "$ref": "#/components/schemas/DateSetting"
          description: "*Conditional:* Returned together with `is_active` when the
            offer has date settings configured."
        validity_end:
          allOf:
          - "$ref": "#/components/schemas/DateSetting"
          description: "*Conditional:* Returned together with `is_active` when the
            offer has date settings configured."
        products:
          type: array
          items:
            "$ref": "#/components/schemas/Product"
          description: "*Conditional:* Included when the offer's products are part
            of the response."
        is_paid:
          type: boolean
          description: "*Conditional:* Included when payment status is determined
            for the current viewer."
      required:
      - id
      - unique_offer_code
      - url
      - title
      - description
      - short_description
      - price_type
      - price
      - has_subscription
      - limit_type
      - limit_of_sales
      - is_draft
    OrderFunnel:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Localized funnel title
          x-translations:
            ru: Локализованное название воронки
            uk: Локалізована назва воронки
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - created_at
    OrderGroup:
      type: object
      properties:
        id:
          type: integer
        slug:
          type: string
        title:
          type: string
          description: Localized group title
          x-translations:
            ru: Локализованное название группы
            uk: Локалізована назва групи
        order:
          type: integer
          description: Position of the group within its funnel
          x-translations:
            ru: Позиция группы внутри её воронки
            uk: Позиція групи всередині її воронки
        created_at:
          type: string
          format: date-time
      required:
      - id
      - slug
      - title
      - order
      - created_at
    OrderStage:
      type: object
      properties:
        id:
          type: integer
        title:
          type: string
          description: Localized stage title
          x-translations:
            ru: Локализованное название этапа
            uk: Локалізована назва етапу
        order_group:
          allOf:
          - "$ref": "#/components/schemas/OrderGroup"
          description: "*Conditional:* Included when the parent group is part of the
            response."
        order_funnel:
          allOf:
          - "$ref": "#/components/schemas/OrderFunnel"
          description: "*Conditional:* Included when the parent funnel is part of
            the response."
        created_at:
          type: string
          format: date-time
      required:
      - id
      - title
      - created_at
    Order:
      type: object
      properties:
        id:
          type: integer
        type_id:
          type: integer
          description: Order type identifier
          x-translations:
            ru: Идентификатор типа заказа
            uk: Ідентифікатор типу замовлення
        first_paid_at:
          type: string
          format: date-time
          nullable: true
        paid_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        products:
          type: array
          items:
            "$ref": "#/components/schemas/Product"
          description: "*Conditional:* Included when the order contains product information."
        payments:
          type: array
          items:
            "$ref": "#/components/schemas/Payment"
          description: "*Conditional:* Included when the order has payment records."
        paid_status:
          type: integer
          description: Payment status identifier
          x-translations:
            ru: Идентификатор статуса оплаты
            uk: Ідентифікатор статусу оплати
          nullable: true
        paid_status_title:
          type: string
          description: Localized title of the payment status
          x-translations:
            ru: Локализованное название статуса оплаты
            uk: Локалізована назва статусу оплати
          nullable: true
        order_stage:
          allOf:
          - "$ref": "#/components/schemas/OrderStage"
          description: "*Conditional:* Included when the order has a stage assigned."
        cost_info:
          allOf:
          - "$ref": "#/components/schemas/Price"
          description: Total cost of the order with currency
          x-translations:
            ru: Полная стоимость заказа с валютой
            uk: Повна вартість замовлення з валютою
        managers:
          type: array
          items:
            "$ref": "#/components/schemas/UserSimple"
          description: "*Conditional:* Included when the order has assigned managers."
        offers:
          type: array
          items:
            "$ref": "#/components/schemas/Offer"
          description: "*Conditional:* Included when the order contains offer information."
        utm:
          allOf:
          - "$ref": "#/components/schemas/UtmList"
          description: "*Conditional:* Included when UTM data is available for the
            order."
      required:
      - id
      - type_id
      - first_paid_at
      - paid_at
      - created_at
      - updated_at
      - paid_status
      - paid_status_title
      - cost_info
    Currency:
      type: object
      properties:
        id:
          type: integer
        code:
          type: string
          description: ISO currency code
          x-translations:
            ru: ISO-код валюты
            uk: ISO-код валюти
          example: USD
        html_code:
          type: string
          description: Localized HTML representation of the currency symbol
          x-translations:
            ru: Локализованное HTML-представление символа валюты
            uk: Локалізоване HTML-представлення символу валюти
          example: "$"
        html_letter_code:
          type: string
          description: Localized HTML representation of the letter currency code
          x-translations:
            ru: Локализованное HTML-представление буквенного кода валюты
            uk: Локалізоване HTML-представлення буквеного коду валюти
          example: USD
      required:
      - id
      - code
      - html_code
      - html_letter_code
    Payment:
      type: object
      properties:
        id:
          type: integer
        status:
          type: string
          description: Payment status identifier
          x-translations:
            ru: Идентификатор статуса оплаты
            uk: Ідентифікатор статусу оплати
          example: paid
        status_title:
          type: string
          description: Localized payment status title
          x-translations:
            ru: Локализованное название статуса оплаты
            uk: Локалізована назва статусу оплати
        payment_type:
          type: string
          description: Payment type identifier (one-time, recurring, etc.)
          x-translations:
            ru: Идентификатор типа оплаты (разовая, рекуррентная и т.д.)
            uk: Ідентифікатор типу оплати (разова, рекурентна тощо)
          nullable: true
        payment_type_title:
          type: string
          description: Localized payment type title
          x-translations:
            ru: Локализованное название типа оплаты
            uk: Локалізована назва типу оплати
          nullable: true
        payment_form:
          type: string
          description: Payment form identifier (online, manual, etc.)
          x-translations:
            ru: Идентификатор формы оплаты (онлайн, вручную и т.д.)
            uk: Ідентифікатор форми оплати (онлайн, вручну тощо)
          nullable: true
        payment_form_title:
          type: string
          description: Localized payment form title
          x-translations:
            ru: Локализованное название формы оплаты
            uk: Локалізована назва форми оплати
          nullable: true
        purchase_type:
          type: string
          description: Purchase type identifier
          x-translations:
            ru: Идентификатор типа покупки
            uk: Ідентифікатор типу покупки
          nullable: true
        purchase_type_title:
          type: string
          description: Localized purchase type title
          x-translations:
            ru: Локализованное название типа покупки
            uk: Локалізована назва типу покупки
          nullable: true
        price_info:
          allOf:
          - "$ref": "#/components/schemas/Price"
          description: Price with currency information
          x-translations:
            ru: Цена с информацией о валюте
            uk: Ціна з інформацією про валюту
        paid_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        transactions:
          type: array
          items:
            "$ref": "#/components/schemas/Transaction"
          description: "*Conditional:* Included when payment transactions are part
            of the response."
      required:
      - id
      - status
      - status_title
      - payment_type
      - payment_type_title
      - payment_form
      - payment_form_title
      - purchase_type
      - purchase_type_title
      - price_info
      - paid_at
      - created_at
      - updated_at
    Price:
      type: object
      properties:
        amount:
          type: number
          description: Raw amount value
          x-translations:
            ru: Сырое значение суммы
            uk: Сире значення суми
          example: 199.99
        amount_rounded:
          type: number
          description: Amount rounded according to cabinet rounding settings
          x-translations:
            ru: Сумма, округлённая по настройкам округления кабинета
            uk: Сума, округлена за налаштуваннями округлення кабінету
          example: 200
        amount_formatted:
          type: string
          description: Amount formatted with the currency symbol/code
          x-translations:
            ru: Сумма с подставленным символом / кодом валюты
            uk: Сума з підставленим символом / кодом валюти
          example: "$199.99"
        amount_formatted_code:
          type: string
          description: Amount formatted with the ISO currency code
          x-translations:
            ru: Сумма с подставленным ISO-кодом валюты
            uk: Сума з підставленим ISO-кодом валюти
          example: 199.99 USD
        currency:
          "$ref": "#/components/schemas/Currency"
      required:
      - amount
      - amount_rounded
      - amount_formatted
      - amount_formatted_code
      - currency
    Transaction:
      type: object
      properties:
        id:
          type: integer
        merchant_id:
          type: integer
          nullable: true
        payment_id:
          type: integer
        order_id:
          type: integer
          nullable: true
        payment_system_status:
          type: string
          description: Raw status string returned by the payment system
          x-translations:
            ru: Сырая строка статуса от платёжной системы
            uk: Сирий рядок статусу від платіжної системи
          nullable: true
        failure_reason:
          type: string
          description: Reason for the transaction failure when applicable
          x-translations:
            ru: Причина неудачи транзакции, если применимо
            uk: Причина невдачі транзакції, якщо застосовно
          nullable: true
        price:
          type: number
        currency_code:
          type: string
          nullable: true
          example: USD
        payer_account:
          type: string
          description: Masked payer account identifier (last digits of card / wallet
            handle)
          x-translations:
            ru: Маскированный идентификатор плательщика (последние цифры карты / handle
              кошелька)
            uk: Маскований ідентифікатор платника (останні цифри картки / handle гаманця)
          nullable: true
        card_mask:
          type: string
          nullable: true
          example: "**** **** **** 4242"
        rrn:
          type: string
          description: Retrieval Reference Number from the bank
          x-translations:
            ru: Retrieval Reference Number от банка
            uk: Retrieval Reference Number від банку
          nullable: true
        fee:
          type: number
          description: Payment system processing fee
          x-translations:
            ru: Комиссия платёжной системы
            uk: Комісія платіжної системи
          nullable: true
        created_at:
          type: string
          format: date-time
      required:
      - id
      - merchant_id
      - payment_id
      - order_id
      - payment_system_status
      - failure_reason
      - price
      - currency_code
      - payer_account
      - card_mask
      - rrn
      - fee
      - created_at
    ProductAggregatedSubscription:
      type: object
      properties:
        is_active:
          type: boolean
          description: Whether access to the product is currently active
          x-translations:
            ru: Активен ли сейчас доступ к продукту
            uk: Чи активний зараз доступ до продукту
        is_paid:
          type: boolean
          description: Whether the subscription is paid (not pre-subscription/trial)
          x-translations:
            ru: Оплачена ли подписка (не пробная)
            uk: Чи оплачена підписка (не пробна)
        start_at:
          type: string
          format: date-time
          nullable: true
        end_at:
          type: string
          format: date-time
          description: Effective end of access (computed across all active subscriptions)
          x-translations:
            ru: Эффективная дата окончания доступа (вычисляется по всем активным подпискам)
            uk: Ефективна дата закінчення доступу (обчислюється за всіма активними
              підписками)
          nullable: true
        offer_end_at:
          type: string
          format: date-time
          nullable: true
        order_end_at:
          type: string
          format: date-time
          nullable: true
        count_available_days:
          type: integer
          description: Total days the contact has access to the product
          x-translations:
            ru: Общее количество дней доступа контакта к продукту
            uk: Загальна кількість днів доступу контакту до продукту
          nullable: true
        count_left_days:
          type: integer
          description: Days of access remaining
          x-translations:
            ru: Осталось дней доступа
            uk: Залишилось днів доступу
          nullable: true
        state:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Current state enum payload
          x-translations:
            ru: Текущее состояние (enum-payload)
            uk: Поточний стан (enum-payload)
      required:
      - is_active
      - is_paid
      - start_at
      - end_at
      - offer_end_at
      - order_end_at
      - count_available_days
      - count_left_days
      - state
    ProductIdentifier:
      type: object
      properties:
        id:
          type: integer
        productable_type:
          type: string
          description: Polymorphic type of the underlying entity (e.g. `course`, `info_unit`)
          x-translations:
            ru: Полиморфный тип нижележащей сущности (`course`, `info_unit` и т.д.)
            uk: Поліморфний тип нижчележачої сутності (`course`, `info_unit` тощо)
        productable_id:
          type: integer
        name:
          type: string
          description: Localized name of the underlying entity
          x-translations:
            ru: Локализованное название нижележащей сущности
            uk: Локалізована назва нижчележачої сутності
        link:
          type: string
          description: Public link to the underlying entity
          x-translations:
            ru: Публичная ссылка на нижележащую сущность
            uk: Публічне посилання на нижчележачу сутність
        course_type_id:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: "*Conditional:* Included when the underlying entity is a course;
            carries the course type enum."
        lessons:
          type: array
          items:
            "$ref": "#/components/schemas/CourseLessonIdentify"
          description: "*Conditional:* Included when course lesson identity payloads
            are loaded for the product."
      required:
      - id
      - productable_type
      - productable_id
      - name
      - link
    ProductSubscription:
      type: object
      properties:
        id:
          type: integer
        creator_id:
          type: integer
          nullable: true
        user_id:
          type: integer
        product_id:
          type: integer
        order_id:
          type: integer
          nullable: true
        offer_id:
          type: integer
          nullable: true
        is_active:
          type: boolean
        start_at:
          type: string
          format: date-time
          nullable: true
        order_end_at:
          type: string
          format: date-time
          nullable: true
        end_at:
          type: string
          format: date-time
          description: Effective end of access for this subscription
          x-translations:
            ru: Эффективная дата окончания доступа для этой подписки
            uk: Ефективна дата закінчення доступу для цієї підписки
          nullable: true
        paid_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - creator_id
      - user_id
      - product_id
      - order_id
      - offer_id
      - is_active
      - start_at
      - order_end_at
      - end_at
      - paid_at
      - created_at
      - updated_at
    Product:
      type: object
      properties:
        id:
          type: integer
        productable_id:
          type: integer
          description: Identifier of the underlying entity (course, info-unit, etc.)
          x-translations:
            ru: Идентификатор нижележащей сущности (курс, info-unit и т.д.)
            uk: Ідентифікатор нижчележачої сутності (курс, info-unit тощо)
        productable_type:
          type: string
          description: Polymorphic type of the underlying entity (e.g. `course`, `info_unit`)
          x-translations:
            ru: Полиморфный тип нижележащей сущности (`course`, `info_unit` и т.д.)
            uk: Поліморфний тип нижчележачої сутності (`course`, `info_unit` тощо)
          example: course
        title:
          type: string
          description: Localized title of the productable entity
          x-translations:
            ru: Локализованное название productable-сущности
            uk: Локалізована назва productable-сутності
        link:
          type: string
          description: "*Conditional:* Included when the underlying entity is part
            of the response. Public link to the productable entity"
          x-translations:
            ru: "*Conditional:* Included when the underlying entity is part of the
              response. Публичная ссылка на productable-сущность"
            uk: "*Conditional:* Included when the underlying entity is part of the
              response. Публічне посилання на productable-сутність"
      required:
      - id
      - productable_id
      - productable_type
      - title
    UserProduct:
      type: object
      properties:
        id:
          type: integer
        productable_id:
          type: integer
        productable_type:
          type: string
          description: Polymorphic type of the underlying entity
          x-translations:
            ru: Полиморфный тип нижележащей сущности
            uk: Поліморфний тип нижчележачої сутності
          example: course
        title:
          type: string
        image_url:
          type: string
          description: URL to the productable preview image
          x-translations:
            ru: URL превью-картинки productable-сущности
            uk: URL превʼю-картинки productable-сутності
          nullable: true
        is_published:
          type: boolean
        aggregated_subscription:
          allOf:
          - "$ref": "#/components/schemas/ProductAggregatedSubscription"
          description: "*Conditional:* Included when subscription summary is part
            of the response. Aggregated access info (overall is_active/start/end across
            all subscriptions)"
          x-translations:
            ru: "*Conditional:* Included when subscription summary is part of the
              response. Агрегированная информация о доступе (общий is_active/start/end
              по всем подпискам)"
            uk: "*Conditional:* Included when subscription summary is part of the
              response. Агрегована інформація про доступ (загальний is_active/start/end
              за всіма підписками)"
        subscriptions:
          type: array
          items:
            "$ref": "#/components/schemas/ProductSubscription"
          description: "*Conditional:* Included when individual subscription records
            are part of the response. Individual subscription records (one per order/offer)"
          x-translations:
            ru: "*Conditional:* Included when individual subscription records are
              part of the response. Отдельные записи подписок (по одной на заказ/предложение)"
            uk: "*Conditional:* Included when individual subscription records are
              part of the response. Окремі записи підписок (по одній на замовлення/пропозицію)"
      required:
      - id
      - productable_id
      - productable_type
      - title
      - image_url
      - is_published
    UtmList:
      type: object
      properties:
        utm_source:
          type: array
          items:
            type: object
          description: Distinct utm_source values seen across the contact's visits
          x-translations:
            ru: Уникальные значения utm_source по всем визитам контакта
            uk: Унікальні значення utm_source за всіма візитами контакту
          example:
          - google
          - facebook
        utm_campaign:
          type: array
          items:
            type: object
        utm_medium:
          type: array
          items:
            type: object
        utm_term:
          type: array
          items:
            type: object
        utm_content:
          type: array
          items:
            type: object
      required:
      - utm_source
      - utm_campaign
      - utm_medium
      - utm_term
      - utm_content
    VisitLocation:
      type: object
      properties:
        country:
          allOf:
          - "$ref": "#/components/schemas/CountrySimple"
          description: Localized country payload
          x-translations:
            ru: Локализованный payload страны
            uk: Локалізований payload країни
          nullable: true
        state_name:
          type: string
          description: State / region name
          x-translations:
            ru: Название штата / региона
            uk: Назва штату / регіону
          nullable: true
        city:
          type: string
          nullable: true
        full_location:
          type: string
          description: Comma-separated `city, state, country` path with empty parts
            skipped
          x-translations:
            ru: Путь `city, state, country` через запятую, пустые части пропускаются
            uk: Шлях `city, state, country` через кому, порожні частини пропускаються
          example: Kyiv, Ukraine
      required:
      - country
      - state_name
      - city
      - full_location
    VisitUserAgent:
      type: object
      properties:
        user_agent:
          type: string
          description: Raw User-Agent header captured at the visit
          x-translations:
            ru: Сырой заголовок User-Agent, зафиксированный на визите
            uk: Сирий заголовок User-Agent, зафіксований на візиті
          nullable: true
        browser:
          type: string
          nullable: true
          example: Chrome
        browser_version:
          type: string
          nullable: true
          example: 120.0.0.0
        platform:
          type: string
          description: Operating system name
          x-translations:
            ru: Название операционной системы
            uk: Назва операційної системи
          nullable: true
          example: macOS
        platform_version:
          type: string
          nullable: true
        device_type_id:
          allOf:
          - "$ref": "#/components/schemas/Enum"
          description: Device type enum payload (desktop, mobile, tablet)
          x-translations:
            ru: 'Тип устройства (enum-payload: desktop, mobile, tablet)'
            uk: 'Тип пристрою (enum-payload: desktop, mobile, tablet)'
        title:
          type: string
          description: Human-readable summary of the user agent
          x-translations:
            ru: Читаемое резюме user agent
            uk: Читаюче резюме user agent
          example: Chrome 120 on macOS
      required:
      - user_agent
      - browser
      - browser_version
      - platform
      - platform_version
      - device_type_id
      - title
    Visit:
      type: object
      properties:
        id:
          type: integer
        ip:
          type: string
          nullable: true
          example: 203.0.113.42
        landing_url:
          type: string
          description: Full URL where the visit landed (auth hash stripped)
          x-translations:
            ru: Полный URL приземления (auth-хеш удалён)
            uk: Повний URL приземлення (auth-хеш видалено)
          nullable: true
        landing_domain:
          type: string
          nullable: true
          example: example.kwiga.com
        landing_path:
          type: string
          nullable: true
          example: "/landing/summer-course"
        landing_params:
          type: string
          description: Query string of the landing URL with auth hash stripped
          x-translations:
            ru: Строка query landing-URL без auth-хеша
            uk: Рядок query landing-URL без auth-хеша
          nullable: true
        referrer_url:
          type: string
          nullable: true
        referrer_domain:
          type: string
          nullable: true
          example: google.com
        utm_source:
          type: string
          nullable: true
          example: google
        utm_campaign:
          type: string
          nullable: true
          example: summer_sale
        utm_medium:
          type: string
          nullable: true
          example: cpc
        utm_term:
          type: string
          nullable: true
        utm_content:
          type: string
          nullable: true
        location:
          type: string
          description: Resolved location label (city, country)
          x-translations:
            ru: Распознанная метка локации (город, страна)
            uk: Розпізнана мітка локації (місто, країна)
          nullable: true
          example: Kyiv, Ukraine
        location_data:
          allOf:
          - "$ref": "#/components/schemas/VisitLocation"
          description: Detailed geolocation breakdown
          x-translations:
            ru: Подробная разбивка по геолокации
            uk: Детальна розбивка за геолокацією
          nullable: true
        device:
          allOf:
          - "$ref": "#/components/schemas/VisitUserAgent"
          description: "*Conditional:* Included when user agent / device data is available
            for the visit. User agent / device information"
          x-translations:
            ru: "*Conditional:* Included when user agent / device data is available
              for the visit. Информация о user agent / устройстве"
            uk: "*Conditional:* Included when user agent / device data is available
              for the visit. Інформація про user agent / пристрій"
        created_at:
          type: string
          format: date-time
      required:
      - id
      - ip
      - landing_url
      - landing_domain
      - landing_path
      - landing_params
      - referrer_url
      - referrer_domain
      - utm_source
      - utm_campaign
      - utm_medium
      - utm_term
      - utm_content
      - location
      - created_at
