openapi: 3.0.3
info:
  title: Lead API
  version: '1.0'
  description: |
    Приём заявок с сайта и внешних форм в ClubIS: создание или обновление анкеты клиента,
    заметки, сделки Roistat.

    Базовый URL: `http://сервер:порт` (методы ниже начинаются с `/api/lead` или `/api/tilda`).

    **Авторизация** — как у остальных методов `/api/*`: в query или заголовках передаются `USER_ID` и `USER_HASH`,
    либо параметр `token` (см. настройки доступа web-service). Без успешной авторизации ответ **401** с телом `Not Authorized!`.

    **Тестовый сервер (test.club-is.ru):** для проверок — `USER_ID=30`, `USER_HASH=f77d565e02169dad60bca51ea19bd53cf1a041d6` (query или заголовки).

    Параметры передаются в **query** (GET) или в теле **application/x-www-form-urlencoded** (POST).
    Значения могут быть URL-encoded. Для телефона и email допускаются варианты `Phone`/`phone`, `Email`/`email`.

    **Настройки (админка / `settings_items`):**
    - `leadapi_always_new_deal` — всегда создавать новую сделку (иначе переиспользовать открытую по анкете).
    - `leadapi_create_event_note` — создавать заметку по заявке.
    - `leadapi_event_type_id` — тип события заметки по умолчанию.
    - `lead_default_club` — клуб при автоматическом создании анкеты.

    **Связанные эндпоинты** с той же логикой обработки, но другим форматом входа:
    `/api/marquiz` (JSON), `/api/webcitrus` (JSON), `/api/gamelead` (form + JSON в `GameLead`, см. описание метода).
servers:
  - url: https://test.club-is.ru
    description: Тестовый сервер web-service
  - url: http://localhost:8080
    description: Локальный web-service

tags:
  - name: Lead
    description: Заявки с сайта и форм

components:
  parameters:
    USER_ID:
      name: USER_ID
      in: query
      required: false
      schema:
        type: string
        example: '30'
      description: ID пользователя ClubIS (альтернатива — заголовок `USER_ID`). Используется как автор создаваемой анкеты/заметки.
    USER_HASH:
      name: USER_HASH
      in: query
      required: false
      schema:
        type: string
        example: f77d565e02169dad60bca51ea19bd53cf1a041d6
      description: Хэш пользователя (альтернатива — заголовок `USER_HASH`).
    token:
      name: token
      in: query
      required: false
      schema:
        type: string
      description: Токен доступа (если настроен для API).
    Phone:
      name: Phone
      in: query
      required: false
      schema:
        type: string
      description: Телефон клиента. Альтернатива — `phone`. Обязателен, если не передан email.
    Email:
      name: Email
      in: query
      required: false
      schema:
        type: string
        format: email
      description: E-mail клиента. Альтернатива — `email`. Обязателен, если не передан телефон.
    Name:
      name: Name
      in: query
      required: false
      schema:
        type: string
      description: Имя. Альтернатива — `title` (если `Name` пуст, берётся `title`).
    Lastname:
      name: Lastname
      in: query
      required: false
      schema:
        type: string
      description: Фамилия.
    Source:
      name: Source
      in: query
      required: false
      schema:
        type: string
        default: Заявка с сайта
      description: Название источника заявки (заголовок сделки).
  schemas:
    LeadOkResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok]
        order_id:
          type: string
          description: ID сделки (`roistat_deals.id`)
        profile_id:
          type: string
          description: ID анкеты клиента (`persons_profiles.id`)
      required:
        - status
        - order_id
    LeadErrorResponse:
      type: object
      properties:
        status:
          type: string
          enum: [error]
        errorString:
          type: string
          description: |
            - `At least one of these parameters must be specified: Phone, Email` — не передан телефон и email.
            - `Order not created` — анкета обработана, но сделка не создана.
      required:
        - status
        - errorString
    LeadFormBody:
      type: object
      description: Тело POST (application/x-www-form-urlencoded). Дополнительные поля формы сохраняются в заметку и в данные сделки.
      properties:
        Phone:
          type: string
        phone:
          type: string
        Email:
          type: string
        email:
          type: string
        Name:
          type: string
        title:
          type: string
        Lastname:
          type: string
        Source:
          type: string
        promotion_id:
          type: integer
          description: ID рекламного источника (`promotions_items.id`)
        promotion:
          type: string
          description: Промокод; при отсутствии `promotion_id` источник может определяться автоматически
        goal_id:
          type: integer
          description: Цель сделки
        user_id:
          type: integer
          description: ID менеджера (`access_users.id`) для назначения на сделку и создаваемую анкету
        status_id:
          type: integer
          description: Статус сделки; при значении вне 1–2 сделка архивируется
        price:
          type: number
          description: Сумма сделки
        cost:
          type: number
          description: Себестоимость сделки
        event_type_id:
          type: integer
          description: Тип события заметки (`contacts_events.id`); переопределяет настройку `leadapi_event_type_id`
        telegram:
          type: string
          description: Telegram; при отсутствии `@` добавляется автоматически
        visit:
          type: string
          description: Roistat visit id
        roistat:
          type: string
          description: Roistat visit id (альтернатива `visit`)
        roistat_url:
          type: string
          description: URL с параметром `roistat` (visit извлекается из query)
        utm_source:
          type: string
        utm_medium:
          type: string
        utm_campaign:
          type: string
        utm_term:
          type: string
        utm_content:
          type: string
        tranid:
          type: string
          description: Номер лида (Tilda)
        formid:
          type: string
          description: ID формы (Tilda)
        WebcitrusSerialNumber:
          type: string
          description: Серийный номер пропуска Webcitrus
        WebcitrusPassNumber:
          type: string
          description: Номер пропуска Webcitrus
    GameLeadJson:
      type: object
      description: |
        JSON в параметре `GameLead` (и дублирующем `json`). Контакт: либо одно поле `contact` (телефон или email),
        либо отдельные `phone` и `email`, если `contact` пуст.
      properties:
        contact:
          type: string
          description: Телефон или email в одном поле (устаревший вариант интеграции)
        phone:
          type: string
          description: Телефон; используется, если `contact` пуст
        email:
          type: string
          format: email
          description: E-mail; используется, если `contact` пуст
        name:
          type: string
        present:
          type: string
          description: Выигранный приз
        ref:
          type: string
          description: Referrer URL; `utm_source` из query подставляется в сделку
        page:
          type: string
        tags:
          type: string
        coupon:
          type: string
          description: Промокод (передаётся как `promotion`)
        unix_time:
          type: string
        roistat_visit:
          type: string

paths:
  /api/lead:
    get:
      operationId: leadSubmitGet
      tags: [Lead]
      summary: Принять заявку (GET)
      description: |
        Создаёт или обновляет анкету, при необходимости — заметку и сделку.
        Удобно для простых интеграций и тестов.
      parameters:
        - $ref: '#/components/parameters/USER_ID'
        - $ref: '#/components/parameters/USER_HASH'
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/Phone'
        - $ref: '#/components/parameters/Email'
        - $ref: '#/components/parameters/Name'
        - $ref: '#/components/parameters/Lastname'
        - $ref: '#/components/parameters/Source'
        - name: phone
          in: query
          schema:
            type: string
        - name: email
          in: query
          schema:
            type: string
        - name: title
          in: query
          schema:
            type: string
        - name: promotion_id
          in: query
          schema:
            type: integer
        - name: promotion
          in: query
          schema:
            type: string
        - name: goal_id
          in: query
          schema:
            type: integer
        - name: user_id
          in: query
          schema:
            type: integer
        - name: status_id
          in: query
          schema:
            type: integer
        - name: price
          in: query
          schema:
            type: number
        - name: cost
          in: query
          schema:
            type: number
        - name: event_type_id
          in: query
          schema:
            type: integer
        - name: telegram
          in: query
          schema:
            type: string
        - name: visit
          in: query
          schema:
            type: string
        - name: roistat
          in: query
          schema:
            type: string
        - name: roistat_url
          in: query
          schema:
            type: string
        - name: utm_source
          in: query
          schema:
            type: string
        - name: utm_medium
          in: query
          schema:
            type: string
        - name: utm_campaign
          in: query
          schema:
            type: string
        - name: utm_term
          in: query
          schema:
            type: string
        - name: utm_content
          in: query
          schema:
            type: string
        - name: tranid
          in: query
          schema:
            type: string
        - name: formid
          in: query
          schema:
            type: string
      responses:
        '200':
          description: |
            Тело — JSON-объект (компактная сериализация). Заголовок ответа: `Content-Type: text/plain; charset=UTF-8`.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LeadOkResponse'
                  - $ref: '#/components/schemas/LeadErrorResponse'
              examples:
                ok:
                  value:
                    status: ok
                    order_id: '12345'
                    profile_id: '67890'
                missingContact:
                  value:
                    status: error
                    errorString: 'At least one of these parameters must be specified: Phone, Email'
                orderNotCreated:
                  value:
                    status: error
                    errorString: Order not created
        '401':
          description: Не авторизован
    post:
      operationId: leadSubmitPost
      tags: [Lead]
      summary: Принять заявку (POST)
      description: |
        Основной сценарий для Tilda и других форм: параметры в теле `application/x-www-form-urlencoded`.
        Логика обработки совпадает с GET.
      parameters:
        - $ref: '#/components/parameters/USER_ID'
        - $ref: '#/components/parameters/USER_HASH'
        - $ref: '#/components/parameters/token'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/LeadFormBody'
            example:
              Phone: '+79001234567'
              Email: client@example.com
              Name: Иван
              Lastname: Иванов
              Source: Заявка с сайта
              tranid: '100500'
              formid: form123
              utm_source: google
      responses:
        '200':
          description: |
            Тело — JSON-объект (компактная сериализация). Заголовок ответа: `Content-Type: text/plain; charset=UTF-8`.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LeadOkResponse'
                  - $ref: '#/components/schemas/LeadErrorResponse'
        '401':
          description: Не авторизован

  /api/tilda:
    post:
      operationId: tildaSubmitPost
      tags: [Lead]
      summary: Принять заявку Tilda (алиас Lead API)
      description: |
        Полный алиас `/api/lead`: тот же обработчик и формат ответа.
        В настройках Tilda указывают URL с `/api/tilda` или `/api/lead`.
      parameters:
        - $ref: '#/components/parameters/USER_ID'
        - $ref: '#/components/parameters/USER_HASH'
        - $ref: '#/components/parameters/token'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/LeadFormBody'
      responses:
        '200':
          description: См. `/api/lead`
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LeadOkResponse'
                  - $ref: '#/components/schemas/LeadErrorResponse'
        '401':
          description: Не авторизован
    get:
      operationId: tildaSubmitGet
      tags: [Lead]
      summary: Принять заявку Tilda (GET, алиас Lead API)
      description: Алиас GET `/api/lead`.
      parameters:
        - $ref: '#/components/parameters/USER_ID'
        - $ref: '#/components/parameters/USER_HASH'
        - $ref: '#/components/parameters/token'
        - $ref: '#/components/parameters/Phone'
        - $ref: '#/components/parameters/Email'
        - $ref: '#/components/parameters/Name'
        - $ref: '#/components/parameters/Lastname'
        - $ref: '#/components/parameters/Source'
      responses:
        '200':
          description: См. `/api/lead`
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LeadOkResponse'
                  - $ref: '#/components/schemas/LeadErrorResponse'
        '401':
          description: Не авторизован

  /api/gamelead:
    post:
      operationId: gameLeadSubmitPost
      tags: [Lead]
      summary: Принять заявку GameLead (POST)
      description: |
        Интеграция игровых виджетов: параметры в теле `application/x-www-form-urlencoded`,
        данные формы — JSON в поле `GameLead` (часто дублируется в `json`).

        Телефон и email: если `contact` в JSON не пуст, он трактуется как телефон или email (по наличию `@`);
        иначе берутся поля `phone` и `email`. Без телефона и email заявка не создаётся (ответ `200` с пустым телом).

        `utm_source` для сделки — из query `ref`, если есть; иначе `GameLead`.
      parameters:
        - $ref: '#/components/parameters/token'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                GameLead:
                  type: string
                  description: JSON-объект (см. `GameLeadJson`)
                json:
                  type: string
                  description: Дубликат `GameLead` у части интеграций
                token:
                  type: string
            example:
              GameLead: '{"phone":"+79001234567","email":"client@example.com","present":"5 полотенец","coupon":"GLU-SLY","ref":"https://example.com/","unix_time":"1782124994"}'
              token: a61bce5a000b56c451b3c312706c9f6a
      responses:
        '200':
          description: |
            При успехе — JSON как у `/api/lead`. Если нет контакта — пустое тело.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/LeadOkResponse'
                  - $ref: '#/components/schemas/LeadErrorResponse'
        '401':
          description: Не авторизован
