openapi: 3.0.3
info:
  title: Uptimo — пользовательский API
  version: "1.0"
  description: |
    REST API для интеграций с CRM Uptimo: контакты, сделки, задачи (канбан).

    **Авторизация:** выпустите токен в личном кабинете (раздел «API и интеграции» на https://uptimo.cloud/dashboard/user-api).

    Передавайте токен в заголовке каждого запроса: `Authorization: Bearer upt_…`

    Базовый URL — тот же, что и у публичного API (например `https://api.uptimo.cloud`).

    **Ошибки:** JSON от NestJS, например `{"statusCode":400,"message":"Текст ошибки"}`; при ошибках валидации `message` может быть массивом строк.
  contact:
    name: Поддержка Uptimo
    email: info@uptimo.cloud

servers:
  - url: https://api.uptimo.cloud
    description: Продакшен
  - url: http://localhost:3000
    description: Локальный API (dev)

tags:
  - name: Контакты CRM
    description: Создание контакта (физлицо или сотрудник компании).
  - name: Сделки
    description: Карточки на доске сделок.
  - name: Задачи (канбан)
    description: Карточки на доске задач.

components:
  securitySchemes:
    bearerUserApi:
      type: http
      scheme: bearer
      description: Токен вида `upt_…`, выданный в ЛК (не JWT-сессия).

  schemas:
    HttpError:
      type: object
      properties:
        statusCode:
          type: integer
          example: 400
        message:
          oneOf:
            - type: string
              example: Колонка не найдена на доске
            - type: array
              items:
                type: string
        error:
          type: string
          example: Bad Request

    CreatePersonRequest:
      type: object
      required: [isClient, isCounterparty]
      properties:
        companyId:
          type: string
          format: uuid
          description: Если указан — контакт привязывается к вашей компании из CRM; иначе нужен `legalStatus`.
        legalStatus:
          type: string
          enum: [ip, self_employed, individual]
          description: Обязателен для контакта **без** `companyId`.
        firstName:
          type: string
          maxLength: 120
        lastName:
          type: string
          maxLength: 120
        middleName:
          type: string
          maxLength: 120
        position:
          type: string
          maxLength: 300
        phone:
          type: string
          maxLength: 64
        email:
          type: string
          maxLength: 320
        telegram:
          type: string
          maxLength: 128
        whatsapp:
          type: string
          maxLength: 128
        isClient:
          type: boolean
        isCounterparty:
          type: boolean
        values:
          type: object
          additionalProperties: true
          description: Дополнительные поля сущности (если настроены в CRM).

    CreateDealRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
          maxLength: 500
        description:
          type: string
          maxLength: 20000
        responsibleUserId:
          type: integer
          minimum: 1
          nullable: true
          description: Исполнитель (пользователь Uptimo — владелец воронки или участник проекта). Если задан без `listId` — сделка попадает в первый этап доски.
        listId:
          type: string
          format: uuid
          nullable: true
          description: Явная колонка доски. Должна существовать на указанной доске.
        amountRub:
          type: number
          nullable: true
        crmPersonId:
          type: string
          format: uuid
          nullable: true
        crmCompanyId:
          type: string
          format: uuid
          nullable: true

    CreateKanbanCardRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
          maxLength: 500
        description:
          type: string
          maxLength: 20000
        assignedUserId:
          type: integer
          minimum: 1
          nullable: true
          description: ID пользователя Uptimo (владелец доски или участник команды доски).
        dueDate:
          type: string
          format: date-time
          nullable: true
        listId:
          type: string
          format: uuid
          nullable: true
          description: Явная колонка. Иначе логика «ответственный → первый этап» или «Неразобранное».

security:
  - bearerUserApi: []

paths:
  /user-api/v1/contacts/persons:
    post:
      tags: [Контакты CRM]
      summary: Создать контакт (персону)
      operationId: createPerson
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePersonRequest"
            examples:
              standalone:
                summary: Физлицо (без компании)
                value:
                  legalStatus: individual
                  firstName: Иван
                  lastName: Петров
                  email: ivan@example.com
                  isClient: true
                  isCounterparty: false
      responses:
        "201":
          description: Контакт создан (тело — как в CRM API `getPerson`, объект персоны с компанией и полями).
          content:
            application/json:
              schema:
                type: object
              example:
                id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                firstName: Иван
                lastName: Петров
                email: ivan@example.com
                isClient: true
                isCounterparty: false
        "400":
          description: Неверные данные (например, нет `legalStatus` без `companyId`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
        "401":
          description: Нет или неверный Bearer-токен.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
              example:
                statusCode: 401
                message: Нужен заголовок Authorization: Bearer upt_…

  /user-api/v1/deals/boards/{boardId}/cards:
    post:
      tags: [Сделки]
      summary: Создать сделку на доске
      operationId: createDeal
      parameters:
        - name: boardId
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Идентификатор доски сделок (вашей).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateDealRequest"
            example:
              title: Сделка из интеграции
              description: Описание
      responses:
        "201":
          description: Сделка создана (сериализованная карточка, как в UI).
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Ошибка бизнес-логики (колонка, сумма, доступ к контакту и т.д.).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
        "401":
          description: Нет или неверный токен.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
        "403":
          description: Нет доступа к доске.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"

  /user-api/v1/kanban/boards/{boardId}/cards:
    post:
      tags: [Задачи (канбан)]
      summary: Создать задачу на доске канбана
      operationId: createKanbanCard
      parameters:
        - name: boardId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateKanbanCardRequest"
            example:
              title: Задача из интеграции
              dueDate: "2026-12-31T12:00:00.000Z"
      responses:
        "201":
          description: Карточка создана.
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Некорректные данные (дата, колонка, исполнитель).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
        "401":
          description: Нет или неверный токен.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HttpError"
