{
  "openapi": "3.0.3",
  "info": {
    "title": "FaxHub API",
    "version": "1.0.0",
    "description": "**PRE-LAUNCH: registration, payments, sending and receiving are NOT open. This is a preview contract. Do not collect credentials or documents, request login codes, or call transactional endpoints. Check https://faxhub.us/launch.json before any operation.**\n\nFaxHub API backed by the existing BMI ERP. Base URL: https://api.faxhub.us/v1.\nThis dedicated contract is built and published by sites/faxhub.us only.\nThe branded API hostname is planned; its production activation has not been verified.\n\nAfter launch, sign in at https://app.faxhub.us using the existing ERP account system (service=faxhub).\nAPI keys use the existing ERP format: `X-API-Key: <id>:<secret>`.\nKey management requires a genuine customer cabinet Bearer session, not another API key.\nX-Account-Id selects an owned billing account returned by GET /accounts.\nPrefer an account-bound key. Existing ERP keys are not automatically fax-only or spend-limited.\n\nPUT creates a fax draft; POST /faxes/{faxId}/send starts real transmission and billing.\nUse send_now=false when creating a draft. Never blindly repeat a write after a timeout.\nCurrently billing occurs before transmission; automatic failure refunds are not implemented.\nApproved launch prices do not establish that backend billing has already been configured.\nIncoming-number purchase, inbound inbox, quotes, agent OAuth and new Stripe offers are\nnot yet represented by implemented FaxHub-specific operations in this contract.",
    "contact": {
      "name": "SIA BMI Group — FaxHub",
      "email": "info@bmi.io",
      "url": "https://faxhub.us/developers/"
    }
  },
  "servers": [
    {
      "url": "https://api.faxhub.us/v1",
      "description": "FaxHub branded gateway (activation pending verification)"
    }
  ],
  "tags": [
    {
      "name": "Faxes",
      "description": "Documents, outbound faxes, receipts and status webhooks"
    },
    {
      "name": "Accounts",
      "description": "Existing ERP customer billing accounts"
    },
    {
      "name": "Cabinet API Keys",
      "description": "Existing ERP API-key lifecycle; interactive cabinet Bearer session required"
    }
  ],
  "paths": {
    "/faxes/files": {
      "post": {
        "operationId": "uploadFaxFile",
        "tags": [
          "Faxes"
        ],
        "summary": "Загрузить вложение",
        "description": "Загрузка документа для факса (multipart/form-data, поле `file`). Файл конвертируется\nв fax-ready PDF (US Letter, 200 dpi, grayscale), считаются страницы, создаётся превью.\n\nПоддерживаемые форматы: PDF, DOC, DOCX, JPG, PNG, TIFF, BMP, HEIC. Лимит размера — 50 МБ.\nПолученный `id` передаётся в `file_ids` при создании факса. Вложения, не привязанные\nк факсу в течение 24 часов, удаляются.\n\nFaxHub gateway limit: the complete HTTP payload, including multipart overhead, must be below 10 MB. The ERP 50 MB limit above does not apply through API Gateway. Large-file direct uploads and asynchronous conversion must be implemented separately before advertising larger uploads.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Документ"
                  },
                  "enhance": {
                    "type": "boolean",
                    "description": "Улучшение фото с камеры (выравнивание освещения); к документам не применять"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Файл сконвертирован и сохранён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxFileUploaded"
                }
              }
            }
          },
          "400": {
            "description": "Файл не передан / неподдерживаемый формат / битый файл / превышен размер"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/faxes/files/{fileId}": {
      "delete": {
        "operationId": "deleteFaxFile",
        "tags": [
          "Faxes"
        ],
        "summary": "Удалить вложение",
        "description": "Удаление загруженного вложения, ещё не привязанного к факсу",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "description": "ID вложения (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Вложение удалено",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Вложение не найдено"
          },
          "409": {
            "description": "Вложение уже привязано к факсу"
          }
        }
      }
    },
    "/faxes/contacts": {
      "get": {
        "operationId": "listFaxContacts",
        "tags": [
          "Faxes"
        ],
        "summary": "Адресная книга",
        "description": "Список контактов-получателей факсов",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Поиск по имени",
            "required": false,
            "schema": {
              "type": "string",
              "example": "acme"
            }
          },
          {
            "name": "page_num",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxContacts"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      },
      "put": {
        "operationId": "createFaxContact",
        "tags": [
          "Faxes"
        ],
        "summary": "Добавить контакт",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Контакт создан",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxContact"
                }
              }
            }
          },
          "400": {
            "description": "Отсутствует name или некорректный fax_number"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/faxes/contacts/{contactId}": {
      "post": {
        "operationId": "updateFaxContact",
        "tags": [
          "Faxes"
        ],
        "summary": "Изменить контакт",
        "description": "Частичное обновление — передаются только изменяемые поля",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "description": "ID контакта",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Контакт обновлён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxContact"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные поля"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Контакт не найден"
          }
        }
      },
      "delete": {
        "operationId": "deleteFaxContact",
        "tags": [
          "Faxes"
        ],
        "summary": "Удалить контакт",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "contactId",
            "in": "path",
            "description": "ID контакта",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Контакт удалён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Контакт не найден"
          }
        }
      }
    },
    "/faxes/coverpages": {
      "get": {
        "operationId": "listFaxCoverPageTemplates",
        "tags": [
          "Faxes"
        ],
        "summary": "Шаблоны cover page",
        "description": "Список сохранённых шаблонов cover page клиента",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxCoverPageTemplates"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      },
      "put": {
        "operationId": "createFaxCoverPageTemplate",
        "tags": [
          "Faxes"
        ],
        "summary": "Добавить шаблон cover page",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxCoverPageTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Шаблон создан",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxCoverPageTemplate"
                }
              }
            }
          },
          "400": {
            "description": "Отсутствует name или sender_name"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/faxes/coverpages/{templateId}": {
      "post": {
        "operationId": "updateFaxCoverPageTemplate",
        "tags": [
          "Faxes"
        ],
        "summary": "Изменить шаблон cover page",
        "description": "Частичное обновление; `is_default: true` делает шаблон дефолтным (снимая флаг с остальных)",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "description": "ID шаблона",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxCoverPageTemplateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Шаблон обновлён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxCoverPageTemplate"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные поля"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Шаблон не найден"
          }
        }
      },
      "delete": {
        "operationId": "deleteFaxCoverPageTemplate",
        "tags": [
          "Faxes"
        ],
        "summary": "Удалить шаблон cover page",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "templateId",
            "in": "path",
            "description": "ID шаблона",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Шаблон удалён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Шаблон не найден"
          }
        }
      }
    },
    "/faxes/webhooks": {
      "get": {
        "operationId": "listFaxWebhooks",
        "tags": [
          "Faxes"
        ],
        "summary": "Список webhooks",
        "description": "Webhooks клиента для событий статусов факсов (secret не возвращается)",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxWebhooks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      },
      "put": {
        "operationId": "createFaxWebhook",
        "tags": [
          "Faxes"
        ],
        "summary": "Добавить webhook",
        "description": "Регистрация URL для событий факсов. Ответ содержит `secret` — он показывается\nЕДИНСТВЕННЫЙ раз, сохраните его. Доставка подписывается заголовком\n`X-FaxHub-Signature: sha256=<HMAC-SHA256(body, secret)>`; событие — в `X-FaxHub-Event`.\nПосле 10 неудачных доставок подряд webhook отключается автоматически.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook создан (с secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxWebhook"
                }
              }
            }
          },
          "400": {
            "description": "Некорректный url (только https) или events"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/faxes/webhooks/{webhookId}": {
      "post": {
        "operationId": "updateFaxWebhook",
        "tags": [
          "Faxes"
        ],
        "summary": "Изменить webhook",
        "description": "Частичное обновление url/events/is_active; включение сбрасывает счётчик неудач",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "ID webhook",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxWebhookRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook обновлён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxWebhook"
                }
              }
            }
          },
          "400": {
            "description": "Некорректный url или events"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Webhook не найден"
          }
        }
      },
      "delete": {
        "operationId": "deleteFaxWebhook",
        "tags": [
          "Faxes"
        ],
        "summary": "Удалить webhook",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "ID webhook",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook удалён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Webhook не найден"
          }
        }
      }
    },
    "/faxes/webhooks/{webhookId}/test": {
      "post": {
        "operationId": "testFaxWebhook",
        "tags": [
          "Faxes"
        ],
        "summary": "Тестовая доставка webhook",
        "description": "Синхронно отправляет тестовое событие `webhook.test` на URL и возвращает результат",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "ID webhook",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Результат доставки",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "boolean",
                      "example": false
                    },
                    "delivered": {
                      "type": "boolean",
                      "description": "true — endpoint ответил 2xx",
                      "example": true
                    },
                    "status": {
                      "type": "string",
                      "description": "HTTP-код или ошибка соединения",
                      "example": "http 200"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Webhook не найден"
          }
        }
      }
    },
    "/faxes/webhooks/{webhookId}/rotate-secret": {
      "post": {
        "operationId": "rotateFaxWebhookSecret",
        "tags": [
          "Faxes"
        ],
        "summary": "Сменить секрет webhook",
        "description": "Генерирует новый секрет подписи; старый перестаёт действовать немедленно. Ответ содержит новый `secret` (единственный показ).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "webhookId",
            "in": "path",
            "description": "ID webhook",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Секрет обновлён (с новым secret)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxWebhook"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Webhook не найден"
          }
        }
      }
    },
    "/faxes/usage": {
      "get": {
        "operationId": "getFaxUsage",
        "tags": [
          "Faxes"
        ],
        "summary": "Расход страниц за месяц",
        "description": "Списанные страницы факсов с начала календарного месяца (UTC)",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxUsage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      }
    },
    "/faxes/receipt/{faxId}": {
      "get": {
        "operationId": "validateFaxReceipt",
        "tags": [
          "Faxes"
        ],
        "summary": "Проверить подлинность квитанции",
        "description": "Публичная проверка подлинности квитанции — этот URL зашит в QR-код на PDF-квитанции.\nАвторизация не требуется; вместо неё обязателен подписанный параметр `key`.\nПодтверждает только факт отправки: в `fax` пустые `file_url` и `preview_url`, `cover_page` = null.",
        "security": [],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "key",
            "in": "query",
            "description": "Подпись из QR-кода",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Квитанция подлинная",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxReceiptValidation"
                }
              }
            }
          },
          "404": {
            "description": "Факс не найден или подпись неверна"
          }
        }
      }
    },
    "/faxes": {
      "get": {
        "operationId": "listFaxes",
        "tags": [
          "Faxes"
        ],
        "summary": "Список факсов",
        "description": "Список факсов клиента за период + агрегированная статистика",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "date_from",
            "in": "query",
            "description": "Период от (включительно). По умолчанию — 30 дней назад",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-07-01"
            }
          },
          {
            "name": "date_to",
            "in": "query",
            "description": "Период до (включительно). По умолчанию — сегодня",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-07-31"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Фильтр по статусу",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "queued",
                "in_progress",
                "sent",
                "error"
              ],
              "example": "sent"
            }
          },
          {
            "name": "to_number",
            "in": "query",
            "description": "Фильтр по номеру получателя",
            "required": false,
            "schema": {
              "type": "string",
              "example": "+18772096018"
            }
          },
          {
            "name": "page_num",
            "in": "query",
            "description": "Номер страницы",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Количество элементов на странице",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Faxes"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          }
        }
      },
      "put": {
        "operationId": "createFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Создать факс",
        "description": "Создание факса-черновика из загруженных вложений (`POST /faxes/files`).\nС `send_now: true` факс сразу ставится в очередь отправки (требуется положительный баланс).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Факс создан",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "400": {
            "description": "Неверный номер / нет файлов / превышен лимит страниц"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "402": {
            "description": "Недостаточно средств на балансе (при send_now)"
          },
          "404": {
            "description": "Вложение или шаблон cover page не найдены"
          }
        }
      }
    },
    "/faxes/{faxId}": {
      "get": {
        "operationId": "getFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Получить факс",
        "description": "Данные факса по его ID",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          }
        }
      },
      "delete": {
        "operationId": "deleteFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Удалить факс",
        "description": "Удаление факса. Факс в очереди или в процессе передачи удалить нельзя — сначала cancel.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Факс удалён",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          },
          "409": {
            "description": "Факс отправляется (queued/in_progress) — удаление запрещено"
          }
        }
      }
    },
    "/faxes/{faxId}/send": {
      "post": {
        "operationId": "sendFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Отправить факс",
        "description": "Постановка факса в очередь отправки. Требуется положительный баланс лицевого счёта.\nОтправить можно факс в статусе `new` или `error` (повторная отправка после ошибки).\nСтраницы факса тарифицируются по тарифному плану (1 страница = 1 минута).",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Факс поставлен в очередь (status → queued)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "402": {
            "description": "Недостаточно средств на балансе"
          },
          "404": {
            "description": "Факс не найден"
          },
          "409": {
            "description": "Недопустимый статус (отправить можно только new или error)"
          }
        }
      }
    },
    "/faxes/{faxId}/cancel": {
      "post": {
        "operationId": "cancelFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Отменить отправку",
        "description": "Отмена запрошенной/запланированной отправки: `queued` → `new` (черновик сохраняется).\nФакс, уже переданный в шлюз (`in_progress`), отменить нельзя.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Отправка отменена (status → new)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          },
          "409": {
            "description": "Факс не в статусе queued"
          }
        }
      }
    },
    "/faxes/{faxId}/duplicate": {
      "post": {
        "operationId": "duplicateFax",
        "tags": [
          "Faxes"
        ],
        "summary": "Дублировать факс",
        "description": "Создание черновика-копии факса (те же вложения, получатель и cover page)",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса для дублирования (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Копия создана (status = new)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          }
        }
      }
    },
    "/faxes/{faxId}/coverpage": {
      "get": {
        "operationId": "getFaxCoverPage",
        "tags": [
          "Faxes"
        ],
        "summary": "Получить cover page факса",
        "description": "Ссылка на PDF cover page (генерируется при первом обращении)",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "URL PDF cover page",
                      "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_cover.pdf"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден или без cover page"
          }
        }
      },
      "put": {
        "operationId": "setFaxCoverPage",
        "tags": [
          "Faxes"
        ],
        "summary": "Задать / убрать cover page",
        "description": "Установка или обновление cover page черновика (status `new`/`error`).\nПустое тело `{}` — убрать cover page. Страницы факса пересчитываются.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FaxCoverPage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cover page обновлена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Fax"
                }
              }
            }
          },
          "400": {
            "description": "Отсутствует sender_name"
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          },
          "409": {
            "description": "Cover page можно менять только у черновика (new/error)"
          }
        }
      }
    },
    "/faxes/{faxId}/receipt": {
      "get": {
        "operationId": "getFaxReceipt",
        "tags": [
          "Faxes"
        ],
        "summary": "Квитанция факса",
        "description": "PDF «Fax Confirmation Report» с параметрами передачи и QR-кодом публичной проверки\nподлинности. Доступна после финального статуса (sent/error); генерируется при первом обращении.",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "faxId",
            "in": "path",
            "description": "ID факса (uuid)",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FaxReceipt"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Факс не найден"
          },
          "409": {
            "description": "Факс ещё не отправлен (квитанция доступна после sent/error)"
          }
        }
      }
    },
    "/accounts": {
      "get": {
        "operationId": "listAccounts",
        "tags": [
          "Accounts"
        ],
        "summary": "Список лицевых счетов",
        "description": "Получение списка лицевых счетов клиента. Закрытые лицевые счета не возвращаются, если у клиента\nесть хотя бы один незакрытый; если незакрытых нет (например, единственный счёт закрыт) —\nвозвращаются все счета клиента. Закрытый счёт по-прежнему доступен по `GET /accounts/{accountId}`\nи через заголовок `X-Account-Id`.\n",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page_num",
            "in": "query",
            "description": "Номер страницы",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Количество элементов на странице",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accounts"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "502": {
            "description": "Не удалось прочитать остаток/лимит из биллинга (временный сбой) — нулевые суммы не отдаются",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/accounts/{accountId}": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Accounts"
        ],
        "summary": "Получить данные лицевого счёта",
        "description": "Возвращает данные лицевого счёта по его ID",
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "accountId",
            "in": "path",
            "description": "ID лицевого счёта",
            "required": true,
            "example": 137154,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/UnauthorizedError"
          },
          "404": {
            "description": "Лицевой счёт не найден"
          },
          "502": {
            "description": "Не удалось прочитать остаток/лимит из биллинга (временный сбой) — нулевые суммы не отдаются",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/apikeys": {
      "get": {
        "operationId": "listApiKeys",
        "tags": [
          "Cabinet API Keys"
        ],
        "summary": "Список API-ключей",
        "description": "Активные и просроченные ключи клиента (отозванные не показываются). Секрет всегда\nмаскирован — полное значение доступно только у одного ключа через `?reveal=1`.\n\nКлючами управляют только из интерактивной сессии ЛК (`Authorization: Bearer`): запрос с\n`X-API-Key` (любым, в том числе действующим) отклоняется 403, в режиме менеджерского входа\nпод клиентом — тоже 403.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "page_num",
            "in": "query",
            "description": "Номер страницы",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "description": "Количество элементов на странице (2..200)",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeysList"
                }
              }
            }
          },
          "401": {
            "description": "Не авторизован"
          },
          "403": {
            "description": "Запрос с X-API-Key («API keys can be managed only from the client cabinet session…») или\nменеджерский вход под клиентом («Not allowed in manager mode»)"
          }
        }
      },
      "put": {
        "operationId": "createApiKey",
        "tags": [
          "Cabinet API Keys"
        ],
        "summary": "Выпустить API-ключ",
        "description": "Самообслуживание: клиент выпускает себе ключ (бессрочный). Ответ содержит полное значение\n`api_key` — единственный момент, когда его отдают без `?reveal=1`. Больше\n10 активных ключей на клиента не выпускается — сначала отзовите неиспользуемые.\n\nТолько из сессии ЛК (`Authorization: Bearer`), не в менеджерском входе. Выпуск пишется в журнал\nавторизации клиента (событие `apikey_created`: учётка, сессия, IP, маска ключа).",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ключ выпущен (в ответе полное значение api_key)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            }
          },
          "400": {
            "description": "Не выбран лицевой счёт при account_bound=true (нужен заголовок X-Account-Id)"
          },
          "401": {
            "description": "Не авторизован"
          },
          "403": {
            "description": "Запрос с X-API-Key или менеджерский вход под клиентом"
          },
          "409": {
            "description": "Достигнут лимит активных ключей (10)"
          },
          "500": {
            "description": "Не удалось создать ключ"
          }
        }
      }
    },
    "/apikeys/{keyId}": {
      "get": {
        "operationId": "getApiKey",
        "tags": [
          "Cabinet API Keys"
        ],
        "summary": "Получить API-ключ",
        "description": "Один ключ клиента. По умолчанию секрет маскирован; `reveal=1` возвращает полное значение\nв поле `api_key` (кнопка «Показать» в ЛК — секрет хранится обратимо, поэтому доступен\nи после создания). Чужой или отозванный ключ — 404.\n\nТолько из сессии ЛК (`Authorization: Bearer`), не в менеджерском входе. Раскрытие (`reveal=1`)\nпишется в журнал авторизации клиента (событие `apikey_revealed`: учётка, сессия, IP, маска ключа).",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "keyId",
            "in": "path",
            "description": "ID ключа",
            "required": true,
            "schema": {
              "type": "integer",
              "example": 42
            }
          },
          {
            "name": "reveal",
            "in": "query",
            "description": "1 — вернуть полное значение ключа в поле api_key",
            "required": false,
            "schema": {
              "type": "integer",
              "enum": [
                0,
                1
              ],
              "example": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                }
              }
            }
          },
          "401": {
            "description": "Не авторизован"
          },
          "403": {
            "description": "Запрос с X-API-Key или менеджерский вход под клиентом"
          },
          "404": {
            "description": "Ключ не найден"
          }
        }
      },
      "delete": {
        "operationId": "revokeApiKey",
        "tags": [
          "Cabinet API Keys"
        ],
        "summary": "Отозвать API-ключ",
        "description": "Ключ немедленно перестаёт авторизовывать запросы и пропадает из списка. Действие\nнеобратимо — восстановить отозванный ключ нельзя, нужно выпустить новый. Отзыв, как выпуск и\nраскрытие, фиксируется в журнале доступа клиента (видно менеджеру в карточке клиента).\n\nТолько из сессии ЛК (`Authorization: Bearer`), не в менеджерском входе.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "keyId",
            "in": "path",
            "description": "ID ключа",
            "required": true,
            "schema": {
              "type": "integer",
              "example": 42
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ключ отозван",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "401": {
            "description": "Не авторизован"
          },
          "403": {
            "description": "Запрос с X-API-Key или менеджерский вход под клиентом"
          },
          "404": {
            "description": "Ключ не найден"
          },
          "500": {
            "description": "Не удалось отозвать ключ (ключ остаётся действующим — повторите запрос)"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-KEY"
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque"
      }
    },
    "responses": {
      "UnauthorizedError": {
        "description": "API key is missing or invalid"
      }
    },
    "schemas": {
      "FaxFileUploaded": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "ID вложения — передаётся в `file_ids` при создании факса",
            "example": "c1f876d4-e18b-44b4-b49e-173397394095"
          },
          "name": {
            "type": "string",
            "description": "Оригинальное имя файла",
            "example": "contract.pdf"
          },
          "mime": {
            "type": "string",
            "description": "Определённый по содержимому MIME-тип",
            "example": "application/pdf"
          },
          "pages": {
            "type": "integer",
            "description": "Число страниц после конвертации в PDF",
            "example": 12
          },
          "preview_url": {
            "type": "string",
            "description": "JPG-превью первой страницы",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_preview.jpg"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "example": "2026-07-03T10:00:00Z"
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string",
            "example": "Operation completed successfully"
          }
        }
      },
      "FaxContact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "name": {
            "type": "string",
            "example": "Acme Corp"
          },
          "company": {
            "type": "string",
            "example": "Acme Inc."
          },
          "fax_number": {
            "type": "string",
            "description": "Номер факса (E.164, без +)",
            "example": "18772096018"
          },
          "phone_number": {
            "type": "string",
            "example": "18775551234"
          },
          "email": {
            "type": "string",
            "example": "fax@acme.com"
          },
          "notes": {
            "type": "string",
            "example": "Main office fax"
          }
        }
      },
      "Navigation": {
        "type": "object",
        "required": [
          "page_size",
          "page_num",
          "page_count"
        ],
        "properties": {
          "page_size": {
            "type": "integer",
            "description": "Количество элементов на странице",
            "example": 50
          },
          "page_num": {
            "type": "integer",
            "description": "Номер текущей страницы",
            "example": 1
          },
          "page_count": {
            "type": "integer",
            "description": "Общее количество страниц",
            "example": 1
          }
        }
      },
      "FaxContacts": {
        "type": "object",
        "required": [
          "data",
          "navigation"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FaxContact"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "FaxContactRequest": {
        "type": "object",
        "required": [
          "name",
          "fax_number"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "Acme Corp"
          },
          "fax_number": {
            "type": "string",
            "description": "Номер факса (7-15 цифр, любые разделители игнорируются)",
            "example": "+1 (877) 209-6018"
          },
          "company": {
            "type": "string",
            "example": "Acme Inc."
          },
          "phone_number": {
            "type": "string",
            "example": "18775551234"
          },
          "email": {
            "type": "string",
            "example": "fax@acme.com"
          },
          "notes": {
            "type": "string",
            "example": "Main office fax"
          }
        }
      },
      "FaxCoverPageTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "name": {
            "type": "string",
            "description": "Название шаблона",
            "example": "Standard"
          },
          "sender_name": {
            "type": "string",
            "example": "John Smith"
          },
          "contact_info": {
            "type": "string",
            "example": "+1 877 209 6018"
          },
          "message": {
            "type": "string",
            "example": "Please find the documents attached."
          },
          "is_default": {
            "type": "boolean",
            "description": "Шаблон по умолчанию (подставляется в новый факс)",
            "example": true
          }
        }
      },
      "FaxCoverPageTemplates": {
        "type": "object",
        "required": [
          "data",
          "navigation"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FaxCoverPageTemplate"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "FaxCoverPageTemplateRequest": {
        "type": "object",
        "required": [
          "name",
          "sender_name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "Standard"
          },
          "sender_name": {
            "type": "string",
            "example": "John Smith"
          },
          "contact_info": {
            "type": "string",
            "example": "+1 877 209 6018"
          },
          "message": {
            "type": "string",
            "example": "Please find the documents attached."
          },
          "is_default": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "FaxWebhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "url": {
            "type": "string",
            "description": "HTTPS URL доставки событий",
            "example": "https://example.com/faxhub/webhook"
          },
          "events": {
            "type": "array",
            "description": "События подписки",
            "items": {
              "type": "string",
              "enum": [
                "fax.sent",
                "fax.error"
              ]
            },
            "example": [
              "fax.sent",
              "fax.error"
            ]
          },
          "is_active": {
            "type": "boolean",
            "description": "false — выключен (вручную или автоматически после 10 неудач подряд)",
            "example": true
          },
          "last_status": {
            "type": "string",
            "description": "Результат последней доставки",
            "example": "http 200"
          },
          "last_sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "example": "2026-07-03T10:00:00Z"
          },
          "secret": {
            "type": "string",
            "nullable": true,
            "description": "Секрет подписи. Возвращается ТОЛЬКО в ответе создания и rotate-secret, в остальных\nответах null. Подпись доставки: заголовок `X-FaxHub-Signature: sha256=<HMAC-SHA256(body, secret)>`.",
            "example": "whsec_1a2b3c..."
          }
        }
      },
      "FaxWebhooks": {
        "type": "object",
        "required": [
          "data",
          "navigation"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FaxWebhook"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "FaxWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "HTTPS URL доставки (обязателен при создании)",
            "example": "https://example.com/faxhub/webhook"
          },
          "events": {
            "type": "array",
            "description": "События подписки (обязательны при создании)",
            "items": {
              "type": "string",
              "enum": [
                "fax.sent",
                "fax.error"
              ]
            }
          },
          "is_active": {
            "type": "boolean",
            "description": "Включить/выключить (только для обновления)",
            "example": true
          }
        }
      },
      "FaxUsage": {
        "type": "object",
        "properties": {
          "month": {
            "type": "string",
            "description": "Календарный месяц (UTC)",
            "example": "2026-07"
          },
          "billed_pages": {
            "type": "integer",
            "description": "Списанных страниц с начала месяца",
            "example": 42
          },
          "included_pages": {
            "type": "integer",
            "nullable": true,
            "description": "Включено страниц в тарифный план (null — план без лимита страниц)",
            "example": null
          }
        }
      },
      "FaxParty": {
        "type": "object",
        "properties": {
          "phone_number": {
            "type": "string",
            "description": "Номер телефона (E.164, без +)",
            "example": "18772096018"
          },
          "station_name": {
            "type": "string",
            "description": "Название станции",
            "example": "FaxHub"
          }
        }
      },
      "FaxCoverPage": {
        "type": "object",
        "required": [
          "sender_name"
        ],
        "properties": {
          "sender_name": {
            "type": "string",
            "description": "Имя отправителя",
            "example": "John Smith"
          },
          "contact_info": {
            "type": "string",
            "description": "Контактные данные отправителя",
            "example": "+1 877 209 6018, john@example.com"
          },
          "message": {
            "type": "string",
            "description": "Сопроводительный текст",
            "example": "Please find the contract documents attached."
          }
        }
      },
      "Fax": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Уникальный ID факса",
            "example": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
          },
          "status": {
            "type": "string",
            "description": "Статус факса:\n* `new` — черновик (создан, не отправлялся)\n* `queued` — отправка запрошена / запланирована, ждёт очереди\n* `in_progress` — передаётся\n* `sent` — успешно доставлен\n* `error` — ошибка (текст в `error`); можно отправить повторно",
            "enum": [
              "new",
              "queued",
              "in_progress",
              "sent",
              "error"
            ],
            "example": "sent"
          },
          "pages": {
            "type": "integer",
            "description": "Количество страниц (включая cover page)",
            "example": 3
          },
          "from": {
            "$ref": "#/components/schemas/FaxParty"
          },
          "to": {
            "$ref": "#/components/schemas/FaxParty"
          },
          "file_url": {
            "type": "string",
            "description": "Ссылка на итоговый PDF факса (пусто, пока документ не собран)",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_final.pdf"
          },
          "preview_url": {
            "type": "string",
            "description": "Ссылка на JPG-превью первой страницы",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_preview.jpg"
          },
          "receipt_url": {
            "type": "string",
            "description": "Ссылка на PDF-квитанцию (пусто, пока не сгенерирована)",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_receipt.pdf"
          },
          "cover_page": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/FaxCoverPage"
              }
            ],
            "description": "Данные cover page (null — без обложки)"
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Текст ошибки для статуса error (для человека, не для ветвления в коде)",
            "example": null
          },
          "error_code": {
            "type": "string",
            "nullable": true,
            "description": "Машинный код ошибки для статуса `error` — по нему и надо ветвить логику и аналитику:\n* `destination_not_allowed` — направление недоступно (v1 — только США и Канада)\n* `insufficient_funds` — не хватило средств на счёте\n* `document_failed` — не удалось собрать итоговый документ\n* `gateway_error` — факс не принят шлюзом\n* `delivery_timeout` — шлюз не отчитался о результате в отведённое время\n* `busy` — занято на стороне получателя\n* `no_answer` — получатель не ответил\n* `transmission_failed` — передача сорвалась (прочие причины, детали в `error`)\n\nСписок расширяется — незнакомый код обрабатывайте как общую ошибку.",
            "enum": [
              "destination_not_allowed",
              "insufficient_funds",
              "document_failed",
              "gateway_error",
              "delivery_timeout",
              "busy",
              "no_answer",
              "transmission_failed"
            ],
            "example": null
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Запланированное время отправки (UTC)",
            "example": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Дата и время создания",
            "example": "2026-07-03T10:00:00Z"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Начало передачи",
            "example": "2026-07-03T10:01:00Z"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Окончание передачи / финальный статус",
            "example": "2026-07-03T10:03:00Z"
          }
        }
      },
      "FaxReceiptValidation": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "example": true
          },
          "fax": {
            "$ref": "#/components/schemas/Fax"
          },
          "receipt_url": {
            "type": "string",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_receipt.pdf"
          }
        }
      },
      "FaxStatistics": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Всего факсов за период",
            "example": 42
          },
          "sent": {
            "type": "integer",
            "description": "Успешно отправленных",
            "example": 35
          },
          "failed": {
            "type": "integer",
            "description": "С ошибкой",
            "example": 3
          },
          "pending": {
            "type": "integer",
            "description": "Черновики / в очереди / передаются",
            "example": 4
          },
          "total_pages": {
            "type": "integer",
            "description": "Всего страниц по всем факсам периода",
            "example": 150
          }
        }
      },
      "Faxes": {
        "type": "object",
        "required": [
          "data",
          "navigation",
          "statistics"
        ],
        "properties": {
          "statistics": {
            "$ref": "#/components/schemas/FaxStatistics"
          },
          "data": {
            "type": "array",
            "description": "Список факсов",
            "items": {
              "$ref": "#/components/schemas/Fax"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "FaxCreateRequest": {
        "type": "object",
        "required": [
          "to_number",
          "file_ids"
        ],
        "properties": {
          "to_number": {
            "type": "string",
            "description": "Номер факса получателя (E.164). В v1 доступны только США и Канада (NANP, код страны 1) —\nномер другого направления отклоняется с ошибкой ещё при создании факса.",
            "example": "+18772096018"
          },
          "file_ids": {
            "type": "array",
            "description": "ID загруженных вложений (`POST /faxes/files`) в порядке склейки",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "example": [
              "c1f876d4-e18b-44b4-b49e-173397394095"
            ]
          },
          "cover_page": {
            "$ref": "#/components/schemas/FaxCoverPage"
          },
          "cover_template_id": {
            "type": "integer",
            "description": "ID шаблона cover page (альтернатива cover_page; поля cover_page перекрывают шаблон)",
            "example": 1
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "description": "Отложенная отправка (UTC, будущее время); отправится после вызова send",
            "example": "2026-07-04T09:00:00Z"
          },
          "send_now": {
            "type": "boolean",
            "description": "true — сразу поставить в очередь отправки (эквивалент POST /faxes/{faxId}/send)",
            "example": false
          }
        }
      },
      "FaxReceipt": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Ссылка на PDF-квитанцию «Fax Confirmation Report»",
            "example": "https://s3.amazonaws.com/files.sip3.net/abc/fax_receipt.pdf"
          },
          "validate_url": {
            "type": "string",
            "description": "Публичная ссылка проверки подлинности (URL из QR-кода на квитанции)",
            "example": "https://rest.bmi.io/v1/faxes/receipt/f47ac10b-...?key=..."
          }
        }
      },
      "Currency": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "ID валюты",
            "example": 1
          },
          "name": {
            "type": "string",
            "description": "Полное наименование валюты",
            "example": "Доллар США"
          },
          "code": {
            "type": "string",
            "description": "Трёхбуквенный код валюты (ISO 4217)",
            "maxLength": 3,
            "example": "USD"
          },
          "symbol": {
            "type": "string",
            "description": "Символ валюты",
            "example": "$"
          }
        }
      },
      "AccountProvider": {
        "type": "object",
        "nullable": true,
        "description": "Наше юрлицо, оказывающее услуги по лицевому счёту (для строки «услуги оказывает …» и\nреквизитов). То же юрлицо, от которого выставляются счета. null — не определено.",
        "properties": {
          "name": {
            "type": "string",
            "nullable": true,
            "description": "Краткое наименование",
            "example": "ООО \"Артагон\""
          },
          "full_name": {
            "type": "string",
            "nullable": true,
            "description": "Полное наименование"
          },
          "legal_form": {
            "type": "string",
            "nullable": true,
            "description": "Организационно-правовая форма"
          },
          "inn": {
            "type": "string",
            "nullable": true,
            "description": "ИНН (для иностранного юрлица — налоговый/рег. номер или null)",
            "example": "7703814060"
          },
          "kpp": {
            "type": "string",
            "nullable": true
          },
          "ogrn": {
            "type": "string",
            "nullable": true,
            "description": "ОГРН / рег. номер",
            "example": "1147746801850"
          },
          "address": {
            "type": "string",
            "nullable": true,
            "description": "Юридический адрес (форматированный)"
          },
          "type": {
            "type": "string",
            "nullable": true,
            "enum": [
              "russian",
              "offshore",
              "private",
              null
            ],
            "description": "russian — РФ-юрлицо, offshore — иностранное",
            "example": "russian"
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "ID лицевого счёта",
            "example": 137154
          },
          "status": {
            "type": "string",
            "description": "Статус лицевого счёта",
            "enum": [
              "active",
              "suspended",
              "closed"
            ],
            "example": "active"
          },
          "name": {
            "type": "string",
            "description": "Наименование лицевого счёта",
            "example": "Основной"
          },
          "id_customer": {
            "type": "integer",
            "description": "ID юридического лица клиента",
            "example": 3001
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "monthly_fee": {
            "type": "number",
            "format": "float",
            "description": "Размер ежемесячной абонентской платы",
            "example": 150
          },
          "balance_opening": {
            "type": "number",
            "format": "float",
            "description": "Входящий баланс на начало текущего месяца. Если в текущем месяце по счёту не было движений,\nравен `balance`.",
            "example": 1351
          },
          "income": {
            "type": "number",
            "format": "float",
            "description": "Сумма поступлений в текущем месяце (0, если движений не было)",
            "example": 500
          },
          "expense": {
            "type": "number",
            "format": "float",
            "description": "Сумма расходов в текущем месяце: наработка + списания (как `spent_this_month` в\nGET /billing/overview); 0, если движений не было",
            "example": 350.5
          },
          "balance": {
            "type": "number",
            "format": "float",
            "description": "Баланс на текущий момент: balance_opening + income − expense. Если в текущем месяце по счёту\nне было движений, — последний известный остаток. Совпадает с `balance` в GET /billing/overview.",
            "example": 1500.5
          },
          "limit": {
            "type": "number",
            "format": "float",
            "description": "Кредитный лимит в знаке биллинга — «дно», ниже которого баланс не может опуститься: 0 — лимита\nнет, иначе ОТРИЦАТЕЛЬНОЕ число (−5000 — кредит 5000). В GET /billing/overview тот же лимит\nотдаётся положительным: `credit_limit` = −limit.",
            "example": -5000
          },
          "remainder": {
            "type": "number",
            "format": "float",
            "description": "Доступно до блокировки: balance − limit (= balance + кредит). Совпадает с `available` в\nGET /billing/overview.",
            "example": 6500.5
          },
          "provider": {
            "$ref": "#/components/schemas/AccountProvider"
          }
        }
      },
      "Accounts": {
        "type": "object",
        "required": [
          "data",
          "navigation"
        ],
        "properties": {
          "data": {
            "type": "array",
            "description": "Список лицевых счетов",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "City not found"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "description": "API-ключ клиента. Полное значение (`api_key`) возвращается только при создании и при\nявном запросе `GET /apikeys/{keyId}?reveal=1` — в списке всегда только маска.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "ID ключа (он же левая часть заголовка X-API-Key)",
            "example": 42
          },
          "key_masked": {
            "type": "string",
            "description": "Ключ с замаскированным секретом: id + первые 4 и последние 4 символа",
            "example": "42:Xq7L••••••••bX9"
          },
          "api_key": {
            "type": "string",
            "description": "Полное значение для заголовка `X-API-Key` (формат `id:secret`). Присутствует ТОЛЬКО\nв ответе на создание и при `?reveal=1`; в списке поля нет.",
            "example": "42:Xq7Lm2Rz9TbV4kPw8NcY3dHf6GsJ1aEuQ5oZbX9"
          },
          "account_id": {
            "type": "integer",
            "nullable": true,
            "description": "Лицевой счёт, к которому ключ жёстко привязан (масштабированный id). null — ключ\nклиентского масштаба: лицевой счёт выбирается заголовком `X-Account-Id` при запросе.",
            "example": 100001
          },
          "active": {
            "type": "boolean",
            "description": "Ключ не отозван и срок его действия не истёк",
            "example": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Когда ключ выпущен (UTC)",
            "example": "2026-08-21T09:15:00Z"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Последнее успешно авторизованное обращение с этим ключом (null — ни разу)",
            "example": "2026-08-21T14:02:11Z"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Срок действия (null — бессрочный; ключи из ЛК выпускаются бессрочными)",
            "example": null
          }
        }
      },
      "ApiKeysList": {
        "type": "object",
        "required": [
          "data",
          "navigation"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKey"
            }
          },
          "navigation": {
            "$ref": "#/components/schemas/Navigation"
          }
        }
      },
      "ApiKeyCreateRequest": {
        "type": "object",
        "description": "Тело выпуска ключа. Пустое тело допустимо — выпустится ключ клиентского масштаба.",
        "properties": {
          "account_bound": {
            "type": "boolean",
            "description": "true — привязать ключ к эффективному лицевому счёту запроса (модель интеграций:\nзаголовок `X-Account-Id` в дальнейшем игнорируется). false/отсутствует — ключ\nклиентского масштаба, лицевой счёт выбирается заголовком при каждом запросе.",
            "default": false,
            "example": true
          }
        }
      }
    }
  },
  "x-launch-status": "prelaunch"
}
