Справочник API «Потока»
Полный список маршрутов: 107 в 21 модуле. Всё живёт под /api.
Это справочник «что есть». Как пройти путь от ключа до оплаченного счёта — в руководстве по интеграции: там сценарий с рабочими curl.
Файл собран из кода и должен ему соответствовать: список маршрутов сверяет
deploy/check-routes.py, и CI падает, если модуль появился, а его раздела
здесь нет.
Два способа авторизоваться
| Кто | Заголовок | Где работает | |
|---|---|---|---|
| JWT | человек в кабинете | Authorization: Bearer <jwt> |
все модули |
| Ключ приложения | чужая система | Authorization: Bearer ooo_sk_… |
счета, контрагенты, вебхуки |
Ключ приложения ограничен скоупами (invoices:read, invoices:write,
counterparties:read, counterparties:write, webhooks:write) — выдавайте
минимальный набор.
Права человека — по политике доступа (docs/research/access-policy.md): у
маршрута указан раздел и уровень (просмотр, изменение, управление) и право,
если оно нужно («Закрывать период», «Выставлять счета и документы»…). Какие
разделы и права у человека, отдаёт GET /api/auth/me (access). Человеку на
маршрутах с ключами скоупы выдаются из его прав: просмотр раздела — :read,
изменение — :write.
Компания запроса
Человек может работать в нескольких компаниях, и роль у него в каждой своя. JWT один на все его компании, а компания запроса выбирается заголовком:
X-Tenant-Id: <id компании>
- Заголовок задан — запрос выполняется в этой компании с ролью человека в ней.
Нет членства в этой компании —
403, даже с валидным JWT. - Заголовка нет — берётся единственная компания человека. Если компаний
несколько, сервер не угадывает:
400. Ни одной —403. - Список компаний и ролей отдаёт
GET /api/auth/me(memberships). - Все эти отказы — с кодом
"code": "tenant_access"рядом сerror: компанию надо выбрать заново (перечитатьmemberships), а не показывать «нет прав». Отказ по правам внутри компании —403с кодом"code": "access_denied": компанию выбирать заново не нужно. - Компания есть, но не работает —
403с"code": "tenant_inactive"и объяснением вerror:pending(зарегистрировалась сама и ждёт подтверждения почты или одобрения оператора),rejected(не прошла проверку, причина — в тексте),suspended(приостановлена). Так же отвечают и ключи приложений такой компании.
Ключу приложения заголовок не нужен и не действует: ключ открывает ровно ту компанию, в которой его выпустили.
Статус оператора платформы (isPlatformAdmin) прав внутри компаний не даёт.
Маршруты
Архив вместо удаления. Контрагенты, договоры, категории, виды платежей,
серии счетов, банковские счета и сотрудники убираются в архив: POST /api/<раздел>/:id/archive
с { "archived": true }, возврат — с false. Ответ — сама запись с
isArchived. Архивная запись остаётся в списках с isArchived: true (поле
выбора должно уметь показать уже выбранную), в новых выборах её не
предлагают. Удаление записи, на которую ссылаются счета, платежи, договоры
или документы, отвечает 409 с archivable: true — такую убирают в архив.
Последствия: архивный банковский счёт не загружает выписку (409 на
/sync) и не входит в тариф; архивная категория не разносит новые платежи;
архивный договор не подбирается к платежам, если его номер не назван в
назначении. Системный вид платежа и основную серию убрать в архив нельзя. Архив
сотрудника — это «уволен» (active: false): зарплата за прошлые месяцы
считается по датам «в штате с/по», поэтому сотрудника с начислениями или
документами не удаляют.
Списки контрагентов и договоров фильтруются параметром archived (1 —
только архивные, 0 — без них).
Бухгалтерия — /api/accounting
Журнал проводок, оборотно-сальдовая ведомость, учётная политика и сверка
остатков на расчётных счетах. Проводки производные: их не вводят, а строят
из событий «Потока» по версионированным правилам (rule, ruleVersion):
операции выписки по виду платежа (51, 57, 58, 60, 62, 66, 68.90, 69, 70,
75, 91); выставленные документы продажи (62.01 / 90.01, НДС 90.03 / 68.02,
сторно при аннулировании в более позднем месяце) и зачёт авансов
62.02 → 62.01 — по привязке оплаты к счёту, иначе FIFO по контрагенту;
закрытые месяцы зарплаты (26 или 44 / 70, 70 / 68.01, 26 или 44 / 69.09),
налог УСН и налог на прибыль из закрытых кварталов (99 / 68.12, 99 / 68.04)
и погашение обязательств с ЕНС (68.xx, 69.09 / 68.90). Незакрытый месяц
зарплаты в журнал не попадает — выплата без начисления даёт предупреждение.
Каждая проводка ссылается на источник: sourceType + sourceId + lineNo.
Займы (/loans): карточка с суммой, сроком и ставкой; на дату выдачи срок больше 12 месяцев — тело на 67, иначе на 66, выданный под проценты — на 58.03, выданный беспроцентный — на 76.09 (своему работнику — 73.01; это дебиторская задолженность, а не финансовое вложение — п. 2 ПБУ 19/02: строка 1230, часть со сроком погашения больше 12 месяцев после отчётной даты — 1190, п. 13–16 ФСБУ 4/2023). Работник — только сотрудник, выбранный в карточке: POST /loans принимает employeeId (только у выданного займа и не у организации с ИНН из 10 цифр — иначе 400), POST /loans/employee { loanId, employeeId | null } ставит или снимает отметку у заведённого займа (404 — займа нет, 409 — заём получен или заёмщик организация, 400 — сотрудника нет). В ответе у займа employeeId, employeeName и employeeHint: { id, name } | null — подсказка «ФИО заёмщика совпало с сотрудником, работавшим на дату договора», счёт она не меняет; рядом с items и candidates приходит employees: [{ id, name, active }] — список для выбора. ПДн: без права «Зарплаты и ПДн сотрудников» (hr.pii; у ключей приложений его нет, и раздел им закрыт) employees пуст и employeesHidden: true, employeeHint — null, у займа сотруднику (отмечен в карточке или заёмщик совпал по имени с сотрудником — то же правило, что у платежей в выписке) counterpartyName и employeeName — «Сотрудник», counterpartyInn пуст, counterpartyId — null, у платежей сотрудникам в candidates получатель — «Выплата сотруднику» / «Поступление от сотрудника»; employeeId, счёт и суммы остаются. POST /loans/employee и POST /loans с employeeId без этого права — 403. Беспроцентный заём, выданный до начала учёта и внесённый вводом остатков на 58.03 с тем же контрагентом, журнал переносит на 76.09 / 73.01 проводкой loan.reclass в день начала учёта (остаток 58.03 без контрагента — предупреждение журнала); в год переноса в checks отчётности есть запись loanReclass — сравнительная графа не пересчитана. У карточки со ставкой 0 без отметки interestFree в ответе — true. principalAccount в карточке — один из этих счетов. Проценты начисляются ежемесячно по дням (Дт 91.02 Кт 66.02/67.02, статья interest — строка 2330). Платёж привязывается POST /loans/payments (purpose: disbursement, principal, interest). В балансе часть 67 к погашению в течение 12 месяцев после отчётной даты переносится в строку 1510 (п. 19 ФСБУ 4/2023), проводкой Дт 67 Кт 66 это не делается.
Займы траншами (financial-statements.md §3.5). POST /loans принимает ещё: counterpartyId (карточка справочника; имя и ИНН берутся из неё, без неё — counterpartyName / counterpartyInn текстом), onDemand (до востребования: dueOn не нужен и хранится как null, тело на 66.01, проценты идут до фактического возврата), interestFree (беспроцентный: ставка сохраняется 0, проводок по процентам нет; annualRateBp: 0 без этого флага — 400), byTranches и tranches: [{ date, amountKopecks }] — выдачи, вписанные руками. При byTranches principalKopecks — сумма по договору (лимит), а тело — фактические выдачи: привязанные платежи disbursement и ещё не закрытые ими tranches; у каждой выдачи счёт 66.01 / 67.01 по сроку от её даты, проценты — на фактический остаток по дням. Привязка платежа disbursement закрывает самые ранние вписанные выдачи на свою сумму. Без byTranches и без tranches заём считается одной суммой с issuedOn, как раньше. POST /loans/tranches { loanId, tranches } заменяет список вписанных выдач (409 у займа одной суммой; 400, если выдача раньше даты договора, не раньше срока или вместе с привязанными платежами больше суммы по договору). В ответе у займа: counterpartyId, onDemand, interestFree, byTranches, tranches, disbursements (date, amountKopecks, transactionId | null, account) и суммы на сегодня — disbursedKopecks, repaidKopecks, outstandingKopecks, accruedInterestKopecks, paidInterestKopecks, unpaidInterestKopecks; dueOn может быть null.
Привязка и предупреждения займов. POST /loans/payments проверяет направление денег: выдача полученного займа и возврат выданного — поступление, выдача нашего займа и возврат полученного — списание, иначе 400 с текстом. Выдачи платежами сверх суммы по договору — не запрет: ответ { items, candidates, warnings: string[] }, в warnings — «выдано больше суммы договора», остаток вписанной руками выдачи, которую платёж закрыл не целиком, и пометка об НДФЛ при привязке уплаты процентов. У займа в ответе warnings: string[] (то же постоянно, пока условие верно) и ndflAgent: boolean — полученный заём под проценты от человека (карточка вида individual, ИНН из 12 цифр без карточки ИП или участник-физлицо из устава без ИНН): компания — налоговый агент по НДФЛ (ст. 226 НК); это только пометка, налог не считается и не проводится. POST /loans/tranches при платежах сверх лимита разрешает уменьшать вписанные выдачи. POST /loans/payments/remove { loanId, transactionId } отвязывает платёж (404, если он к этому займу не привязан): проводка по нему снова строится по виду платежа. Ответ — { items, candidates, warnings: string[] }. Вписанные выдачи, которые платёж закрыл при привязке, возвращаются в tranches ровно такими, какими он их закрыл (закрытая часть — к оставшейся выдаче на ту же дату): при привязке это запоминается, и у платежа в карточке payments[].restoresTranches: [{ date, amountKopecks }] — что вернёт отвязка ([] — ничего). У выдач займа траншами, привязанных до этого, restoresTranches: null: отвязка работает как раньше, а в warnings — «вписанная выдача не восстановлена — впишите её заново, если деньги были». Вид платежа отвязка не меняет: если он остаётся видом «заём» (loan_in / loan_return_out — журнал держит его на 66.01, loan_out / loan_return_in — на 58.03, без карточки срок и беспроцентность неизвестны) и ни к одному займу не привязан, в warnings ответа и в warnings карточки, от которой его отвязали, — «платёж остался займом в учёте — привяжите его к займу или смените вид платежа»; из карточки предупреждение уходит, когда платёж снова привязан или у него сменили вид. Аналитика «контрагент» у привязанных платежей и у начисленных процентов — одна, из карточки займа (ИНН, без него — name:<название>), а не из выписки.
Основные средства (/assets): карточка с инвентарным номером, датой ввода, стоимостью и сроком. Ввод Дт 01 Кт 08 (или 10); ниже лимита учётной политики — сразу Дт 26/44 Кт 08/10. Линейная амортизация Дт 26/44 Кт 02 с месяца, следующего за вводом. Выбытие — остаточная стоимость в 91.02. Налоговый учёт ОС при УСН «доходы минус расходы» (п. 3 ст. 346.16) не ведётся.
Товары (/inventory): приход с количеством и суммой, себестоимость по средней, списание Дт 90.02 Кт 41. Строка продажи сопоставляется с товаром по артикулу или названию; не сопоставилась — предупреждение и ручное списание. Входящий документ на 41 без позиций становится одной позицией «товары» количеством 1.
Дивиденды (/dividends): решение делит сумму по долям участников (Дт 84.01 Кт 75.02). НДФЛ физлицам — 13 % до 2,4 млн ₽ дивидендов за год и 15 % сверху (п. 1.1 ст. 224 НК), Дт 75.02 Кт 68.01; срок уплаты — п. 6 ст. 226, в ответе ndflDueOn. Организации — налог на прибыль у источника (п. 3 ст. 275): 0 %, если на дату решения доля не меньше 50 % и владение не меньше 365 дней (пп. 1 п. 3 ст. 284), иначе 13 % (пп. 2). Дата владения — поле участника ownedFrom. Проводка Дт 75.02 Кт 68.04 появляется после POST /dividends/pay (paidOn); к выплате нетто. Срок уплаты — п. 4 ст. 287 (profitTaxDueOn), расчёт КНД 1151006 лист 03 — ст. 289 (profitTaxReportDueOn). ООО на УСН тоже агент. Выплата видом dividends с ИНН участника закрывает 75.02. Предупреждения (чистые активы, нераспределённая прибыль) не блокируют проведение.
GET /api/accounting/account-card?account&from&to — карточка счёта: сальдо на
начало, проводки с сальдо после каждой и сальдо на конец; account — субсчёт
или синтетический счёт со всеми субсчетами, аналитика (counterparty,
bankAccount, contract, employee, transit, item) — в строке запроса и
сужает карточку до одной строки ОСВ; проводки страницами (offset, limit до
500; total — всего), итоги — по всему периоду.
GET /api/accounting/trial-balance/xlsx?from&to — ОСВ в Excel с итоговой
строкой и проверкой Σ Дт = Σ Кт. GET /api/accounting/journal/xlsx?from&to&account
— журнал проводок; GET /api/accounting/account-card/xlsx?account&from&to и
GET /api/accounting/account-analysis/xlsx?account&from&to — карточка счёта
(проводки с сальдо после каждой, итоги за период) и анализ счёта (обороты по
корреспондирующим счетам) одним файлом, без постраничности; аналитика — в
строке запроса, как у карточки на экране. Во всех четырёх файлах суммы —
числами в рублях, даты проводок — датами Excel (формат «дд.мм.гггг»: работают
фильтр и сортировка), предупреждения журнала — те же, что на экране, —
строками «Внимание: …» под шапкой; в шапке — название компании с ИНН/КПП,
период и дата формирования (по часовому поясу компании). План счетов —
CHART_OF_ACCOUNTS в @ooo/shared.
Без права «Зарплаты и ПДн сотрудников» в ОСВ, её выгрузке, журнале, карточке
и их выгрузках нет аналитики employee (счета 70 и 71 — одной строкой,
свёрнутым сальдо; обороты те же), фильтр карточки по employee не действует,
а примечание ввода остатков по сотруднику заменено на «Ввод остатков»
(access-policy.md §6.3).
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/accounting/trial-balance |
JWT; раздел «Бухгалтерия» — просмотр |
GET |
/api/accounting/journal |
JWT; раздел «Бухгалтерия» — просмотр |
GET |
/api/accounting/account-card |
JWT; раздел «Бухгалтерия» — просмотр |
GET |
/api/accounting/trial-balance/xlsx |
JWT; раздел «Бухгалтерия» — просмотр, право «Массовые выгрузки» |
GET |
/api/accounting/journal/xlsx |
JWT; раздел «Бухгалтерия» — просмотр, право «Массовые выгрузки» |
GET |
/api/accounting/account-card/xlsx |
JWT; раздел «Бухгалтерия» — просмотр, право «Массовые выгрузки» |
GET |
/api/accounting/account-analysis/xlsx |
JWT; раздел «Бухгалтерия» — просмотр, право «Массовые выгрузки» |
GET |
/api/accounting/policy |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/policy |
JWT; раздел «Бухгалтерия» — изменение |
GET |
/api/accounting/bank-balances |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/bank-balances |
JWT; раздел «Бухгалтерия» — изменение |
GET |
/api/accounting/loans |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/loans |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/loans/payments |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/loans/payments/remove |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/loans/tranches |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/loans/employee |
JWT; раздел «Бухгалтерия» — изменение, право «Зарплаты и ПДн сотрудников» |
POST |
/api/accounting/loans/remove |
JWT; раздел «Бухгалтерия» — управление |
GET |
/api/accounting/assets |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/assets |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/assets/dispose |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/assets/remove |
JWT; раздел «Бухгалтерия» — управление |
GET |
/api/accounting/inventory |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/inventory |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/inventory/receipts |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/inventory/issues |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/inventory/remove |
JWT; раздел «Бухгалтерия» — управление |
GET |
/api/accounting/dividends |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/dividends |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/dividends/pay |
JWT; раздел «Бухгалтерия» — изменение |
POST |
/api/accounting/dividends/remove |
JWT; раздел «Бухгалтерия» — управление |
GET |
/api/accounting/charter |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/charter |
JWT; раздел «Бухгалтерия» — изменение |
GET |
/api/accounting/opening |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/opening |
JWT; раздел «Бухгалтерия» — изменение, право «Закрывать период» |
DELETE |
/api/accounting/opening |
JWT; раздел «Бухгалтерия» — изменение, право «Закрывать период» |
GET |
/api/accounting/years |
JWT; раздел «Бухгалтерия» — просмотр |
POST |
/api/accounting/years/close |
JWT; раздел «Бухгалтерия» — изменение, право «Закрывать период» |
POST |
/api/accounting/years/reopen |
JWT; раздел «Бухгалтерия» — изменение, право «Закрывать период» |
GET |
/api/accounting/statements |
JWT; раздел «Бухгалтерия» — просмотр |
GET |
/api/accounting/statements/pdf |
JWT; раздел «Бухгалтерия» — просмотр |
GET |
/api/accounting/statements/xml |
JWT; раздел «Бухгалтерия» — просмотр, право «Сдавать отчётность» |
GET |
/api/accounting/statements/notes |
JWT; раздел «Бухгалтерия» — просмотр, право «Сдавать отчётность» |
trial-balance?from=ГГГГ-ММ-ДД&to=ГГГГ-ММ-ДД — ОСВ: по каждому счёту сальдо
на начало, обороты и сальдо на конец (debitKopecks / creditKopecks),
развёрнутые по аналитике в rows (контрагент, расчётный счёт, договор,
операция в пути); balanced — Σ Дт = Σ Кт по всем колонкам; warnings —
операции без вида, переводы между своими счетами без пары. journal — те же
даты и необязательный account («60» — со всеми субсчетами, «60.02» —
только он); счёт не из плана — 400.
policy — учётная политика с даты effectiveFrom: метод признания
(method: accrual — по отгрузке, cash — по оплате), счёт затрат
(costAccount: 26 или 44), мелкие расходы без документа по оплате
(expensesOnPayment), куда относить комиссии банка и посредников
(commissions: cost или other — 91.02), лимит ОС (fixedAssetLimitKopecks)
и право на упрощённые способы бухучёта (simplifiedMethods). GET —
current и periods; пока политики нет, действует политика по умолчанию
(id: null, с 2000-01-01). POST заводит период или правит период на ту
же дату; кассовый метод без simplifiedMethods — 400.
bank-balances?date=ГГГГ-ММ-ДД (по умолчанию сегодня) — по каждому
расчётному счёту расчётный остаток (сальдо 51 на конец дня), остаток банка
на эту или ближайшую раннюю дату (bankDate), разница на дату остатка банка
и status: ok, mismatch, unknown (остатка банка нет). POST с
bankAccountId, date, balanceKopecks — остаток из выписки на конец дня;
повторный ввод на ту же дату — правка; счёт чужой компании — 404.
charter — уставный капитал (charterCapitalKopecks), дата регистрации и
участники (name, inn, sharePercent; сумма долей — 100 %, иначе 400).
POST сохраняет карточку целиком. В ответе у участника вклад по доле и
сколько внесено и осталось — по сальдо 75.01 в журнале (оплата вида «Взнос
учредителя» с ИНН участника гасит его долг), и posting: journal —
Дт 75.01 Кт 80 датой регистрации, opening — ООО зарегистрировано раньше
даты начала учёта, капитал вводится в остатки, none — нет суммы или даты.
opening — ввод остатков на дату начала учёта (date): строки lines
(section — balances, documents, turnovers; account, side,
amountKopecks, аналитика bankAccountId, counterpartyInn,
counterpartyName, contractId, employeeId, note) и прошлогодний
баланс priorYear для сравнительной графы. Каждая строка — проводка против
счёта 000 последним днём перед датой начала; события раньше даты начала в
журнал не входят. POST сохраняет всё целиком; сальдо 000 не ноль — 400 с
differenceKopecks; обороты 90/91/99 при старте с 1 января — 400; ссылка
на чужой счёт, договор или сотрудника — 404. GET — строки, итоги и
проверки checks (ok или warn): остаток 51 против остатка банка на день
перед началом, уставный капитал в остатках, операции до даты начала,
прошлогодний баланс. Без права «Зарплаты и ПДн сотрудников» строки счетов
70, 71 и 73 и любые строки с employeeId свёрнуты по счёту и стороне:
employeeId пуст, примечание и имя контрагента строки очищены, сумма счёта
та же. DELETE убирает ввод остатков (нет — 404).
years?year=ГГГГ — закрытие бухгалтерского года: closed (кто и когда
закрыл, чистая прибыль, число проводок в снимке), previewNetProfitKopecks —
результат, который даст реформация, и history закрытий. POST /years/close
с year проводит реформацию на 31.12 (субсчета 90 и 91 → 90.09 и 91.09 →
99 → 84.02) и сохраняет проводки года и ОСВ снимком; с 1 января следующего
года 84.02 переносится на 84.01. Закрыть можно только закончившийся год
(409), по порядку — незакрытый прошлый год с доходами и расходами даёт
409, повторное закрытие — 409. Журнал за закрытый год читает снимок:
правка операций прошлого года его не меняет, а даёт предупреждение со
списком счетов. POST /years/reopen с year и reason открывает год
заново (не закрыт — 404, закрыт следующий — 409).
statements?year=ГГГГ — упрощённая бухгалтерская отчётность (ФСБУ 4/2023,
приложение 9): баланс на 31 декабря отчётного, предыдущего и позапрошлого
года (balanceDates) и ОФР за отчётный и предыдущий год (pnlYears). У
строки code — код показателя с наибольшим значением в группе, kopecks и
thousands по графам (null — данных нет, в форме прочерк), expense —
расход в скобках. Графа прошлого года — прошлогодний баланс из ввода
остатков, если он есть, иначе журнал (priorSource). checks — 1600 = 1700,
рост нераспределённой прибыли = чистая прибыль − дивиденды, деньги против
выписки, закрыт ли год; incomplete — баннер, если есть остатки ОС, материалов
или товаров; notes — пояснения по п. 54 ФСБУ 4/2023. Предприниматель
отчётность не сдаёт — 409. statements/pdf — печатная форма.
statements/xml — файл для ГИР БО по формату 5.05 (приказ ФНС
ЕД-1-1/326@, КНД 0710096) в windows-1251 с именем
NO_BOUPR_A_K_O_ГГГГММДД_N.xml; statements/notes — PDF пояснений с именем,
на которое ссылается XML того же дня. Без КПП, кода налоговой, адреса или ФИО
руководителя в настройках — 409 со списком missing.
Входящие закрывающие документы библиотеки (/api/documents) — регистр
расходов: у них vatKopecks, expenseAccount (26, 44, 91.02, 08, 10, 41;
пусто — счёт затрат учётной политики) и payment — оплата по журналу
(paidKopecks, status: paid, partial, unpaid). Проводки: Дт статья
Кт 60.01 датой документа (на ОСНО НДС — Дт 19), зачёт аванса поставщику
Дт 60.01 Кт 60.02 — сначала привязанными к документу оплатами, затем по
поставщику.
Ключи приложений — /api/api-clients
Ключ показывается ОДИН раз — при создании. В базе лежит только хеш и последние 4 символа.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/api-clients |
JWT; право «Интеграции» |
POST |
/api/api-clients |
JWT; право «Интеграции» |
PATCH |
/api/api-clients/:id |
JWT; право «Интеграции» |
POST |
/api/api-clients/:id/revoke |
JWT; право «Интеграции» |
Аутентификация — /api/auth
Регистрация своей компании — POST /signup: { email, password, name, companyName, inn, termsAccepted, pdConsent }. Две отметки формы, обе — только true: termsAccepted — человек принял Оферту и Условия использования, pdConsent — дал отдельное согласие на обработку персональных данных по Политике (ст. 9 152-ФЗ). Без любой из них или с другим значением — 400 («Нужно принять Оферту и Условия использования» или «Нужно согласие на обработку персональных данных»), учётка не создаётся. При успехе на человеке сохраняются дата согласия, версия политики, IP и User-Agent (pdConsent*), а в legalAcceptances — редакции всех четырёх документов (offer, terms, pd-consent, privacy) с той же датой, IP и User-Agent. Редакции ставит сервер — те, что опубликованы сейчас (/legal/<slug>). Заводит учётную запись, компанию в статусе pending и членство-администратора, отвечает 201 с сессией (token, user) и mailSent — ушло ли письмо со ссылкой подтверждения. Компания работает только после двух шагов: человек подтверждает почту (POST /verify-email/:token по ссылке из письма — тогда компания попадает в очередь оператора) и оператор её одобряет. До этого данные компании закрыты и для владельца (см. «Компания запроса»). Отказы: ИНН с неверной контрольной суммой — 400 с fields.inn; компания с этим ИНН уже есть — 409 «Компания уже зарегистрирована — попросите её администратора пригласить вас»; почта занята — 409 с fields.email; больше 10 регистраций с одного IP за сутки — 429. Неподтверждённая регистрация ИНН не держит: через 3 дня (срок ссылки) его можно зарегистрировать заново. POST /verify-email/resend — новая ссылка, не чаще раза в минуту (429), прежняя перестаёт работать.
В проде регистрация открыта, только когда настроена почта (SMTP_HOST и MAIL_FROM): без письма почту не подтвердить. Иначе — 503, а GET /setup отдаёт signupOpen: false.
/register открыт, только пока в базе нет пользователей, — так заводится первый администратор (он же оператор платформы) при развёртывании с нуля; GET /setup говорит, открыт ли он (registrationOpen). Дальше POST /register отвечает 403, а сотрудников в компанию добавляет её администратор. POST /password — сменить свой пароль, с проверкой текущего.
POST /forgot-password — письмо со ссылкой для смены пароля. Тело { email }; ответ всегда одинаковый { ok: true }, есть такая почта или нет (иначе форма перебирает адреса). Отказы: 503 в проде без настроенной почты (как регистрация); 429 — чаще раза в минуту на один адрес или больше 10 запросов с одного IP за сутки (лимиты одинаковы для любой почты). POST /reset-password — новый пароль по ссылке из письма: { token, newPassword }, пароль — от 6 символов. Ссылка одноразовая и живёт час; неверная или использованная — 404, просроченная — 410. Вместе со сменой ставится passwordChangedAt: выданные до него JWT перестают действовать — все прежние сессии разрываются.
/login, /register, /signup и /me отдают профиль: memberships — компании, роль в каждой и статус компании (tenantStatus, для отказа и приостановки — tenantStatusNote), emailVerified — подтверждена ли почта, tenantId и role — активная компания (из X-Tenant-Id, а без него единственная; null, если компаний несколько). Права — по политике доступа (docs/research/access-policy.md): у каждого членства profile (full, accountant, secretary, sales, hr, observer), overrides — число поправок к профилю и access — итоговые права { profile, sections: { <раздел>: none|view|edit|manage }, actions: [...] }; access верхнего уровня — права в активной компании (null, если она не выбрана). role — переходное поле, вычисляется из профиля. Отказ по правам — 403 { error, code: "access_denied" }. /me, /password и /verify-email/resend работают без выбора компании.
| Метод | Путь | Доступ |
|---|---|---|
POST |
/api/auth/login |
без авторизации |
GET |
/api/auth/me |
JWT |
POST |
/api/auth/password |
JWT |
GET |
/api/auth/setup |
без авторизации |
POST |
/api/auth/register |
без авторизации |
POST |
/api/auth/signup |
без авторизации |
POST |
/api/auth/verify-email/:token |
без авторизации (токен из письма) |
POST |
/api/auth/verify-email/resend |
JWT |
POST |
/api/auth/forgot-password |
без авторизации |
POST |
/api/auth/reset-password |
без авторизации (токен из письма) |
Банковские счета — /api/bank-accounts
Токен доступа к банку шифруется AES-256-GCM на BANK_TOKEN_KEY и наружу не отдаётся. POST /:id/sync тянет выписку и попутно заводит новых контрагентов. PUT /:id/token меняет токен у подключённого счёта без потери истории: новый токен сначала проверяется в банке, при отказе прежний остаётся. Номер счёта уникален в пределах компании — второе подключение того же счёта даёт 409; счёт с загруженными операциями не удаляется. Токен Т-Банка привязан к IP: если банк его отверг (401/403), к тексту банка добавляется подсказка проверить, что IP «Потока» (egressIps из /api/onboarding) разрешён для токена в Т-Бизнесе. У каждого счёта free — бесплатный ли он по тарифу (см. «Тариф»). PATCH /:id правит то, чего нет в API банка: своё название счёта, название и город банка (bankCity — печатается в шапке счёта: «АО «ТБанк» г. Москва») и корсчёт. Номер счёта и токен так не меняются: номер — это другой счёт, для токена есть PUT /:id/token.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/bank-accounts |
JWT; раздел «Банковские счета» — просмотр |
POST |
/api/bank-accounts |
JWT; раздел «Банковские счета» — изменение |
PATCH |
/api/bank-accounts/:id |
JWT; раздел «Банковские счета» — изменение |
DELETE |
/api/bank-accounts/:id |
JWT; раздел «Банковские счета» — управление |
POST |
/api/bank-accounts/:id/archive |
JWT; раздел «Банковские счета» — изменение |
POST |
/api/bank-accounts/:id/sync |
JWT; раздел «Банковские счета» — изменение |
PUT |
/api/bank-accounts/:id/token |
JWT; раздел «Банковские счета» — изменение |
Тариф — /api/billing
Первый подключённый расчётный счёт — бесплатно, каждый следующий — 1000 ₽ в месяц, оплата вперёд счётом из самого «Потока». Бесплатные — самые ранние по дате подключения: удалили бесплатный — бесплатным становится следующий. GET /api/billing — сводка компании: accounts, paidAccounts, monthlyKopecks, freeAccountIds, paidUntil (оплачено по), dueOn (срок выставленного счёта за тариф) и status:
free— платных счетов нет;ok— всё оплачено;due— счёт за тариф выставлен, срок не наступил;overdue— срок прошёл, идут 7 дней отсрочки (blockedFrom— когда кончатся);blocked— отсрочка кончилась: платные счета не синхронизируются (POST /api/bank-accounts/:id/syncотвечает402, причина — вlastSyncErrorсчёта), бесплатный работает, ничего не удаляется. После оплаты выписка догрузится сама.
Пока счёт за тариф просрочен (overdue и blocked), компания не расширяется: POST /api/bank-accounts, POST /api/api-clients, POST /api/webhooks и POST /api/invites отвечают 402 с объяснением. Счета, платежи, сверка и отчёты работают как обычно. Владельцу приходят два письма на счёт: в день срока и в день остановки платных счетов.
Счёт за тариф выставляет сам «Поток» — из своей компании в «Потоке» (PLATFORM_TENANT_ID), в первую неделю месяца, на месяц вперёд: количество — платные счета, подключённые до начала месяца (подключённый в середине месяца работает сразу, а в счёт попадает со следующего первого числа). Счёт уходит владельцу письмом с PDF; оплату отмечает обычная сверка по выписке. В сводке — unpaidInvoice: номер, сумма и сколько уже пришло; PDF — GET /api/billing/invoices/:id/pdf (только своей компании, чужой — 404). Частичная оплата долг не закрывает; отвязанный платёж и аннулирование счёта пересчитывают долг.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/billing |
JWT; любой член компании |
GET |
/api/billing/invoices/:id/pdf |
JWT; любой член компании |
Категории — /api/categories
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/categories |
JWT; раздел «Справочники» — просмотр |
POST |
/api/categories |
JWT; раздел «Справочники» — изменение |
DELETE |
/api/categories/:id |
JWT; раздел «Справочники» — управление |
POST |
/api/categories/:id/archive |
JWT; раздел «Справочники» — изменение |
PATCH |
/api/categories/:id |
JWT; раздел «Справочники» — изменение |
Договоры и оферты — /api/contracts
Основание платежа и счёта: договор, оферта или счёт-оферта. У оферты бумаги нет — есть ссылка на условия на сайте поставщика и дата акцепта (оплаты).
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/contracts |
JWT; раздел «Договоры» — просмотр |
POST |
/api/contracts |
JWT; раздел «Договоры» — изменение |
GET |
/api/contracts/:id |
JWT; раздел «Договоры» — просмотр |
PATCH |
/api/contracts/:id |
JWT; раздел «Договоры» — изменение |
DELETE |
/api/contracts/:id |
JWT; раздел «Договоры» — управление |
POST |
/api/contracts/:id/archive |
JWT; раздел «Договоры» — изменение |
GET |
/api/contracts/:id/periods |
JWT; раздел «Договоры» — просмотр |
PATCH |
/api/contracts/:id/periods/:key |
JWT; раздел «Договоры» — изменение |
POST |
/api/contracts/:id/document |
JWT; раздел «Договоры» — изменение |
GET |
/api/contracts/:id/related |
JWT; раздел «Договоры» — просмотр |
GET |
/api/contracts/:id/tree |
JWT; раздел «Договоры» — просмотр |
GET |
/api/contracts/:id/events |
JWT; раздел «Договоры» — просмотр |
GET |
/api/contracts/:id/snapshots |
JWT; раздел «Договоры» — просмотр |
POST |
/api/contracts/:id/snapshot |
JWT; раздел «Договоры» — изменение |
POST |
/api/contracts/match |
JWT; раздел «Договоры» — изменение |
Фильтры списка: kind, side (buyer — мы покупатель, supplier — мы
поставщик), counterpartyId, q, active (1 — действующие, 0 —
закончившиеся).
В каждом ответе есть totals: paidKopecks — сколько денег прошло по
договору, closedKopecks — на сколько получено закрывающих документов (акты,
накладные, УПД, чеки), плюс счётчики платежей, документов и счетов. Признак
active считается на лету: по сроку, автопролонгации и дате закрытия.
Периодические договоры. У договора есть periodicity (once, monthly,
quarterly, yearly), periodAmountKopecks — ожидаемая сумма за период и
settlementDay — до какого числа платят. GET /api/contracts/:id/periods
возвращает ряд периодов по сегодняшний день: ключ (2026-09, 2026-Q3,
2026), границы, ожидаемая сумма, что заплачено, чем закрыто и статус
(current, ok, unpaid, unclosed, unpaid_unclosed). Ряд считается из
периодичности и срока — в базе он не хранится; автопролонгация продолжает его
за срок действия, будущих периодов нет.
Сумма периода правится отдельно — PATCH /api/contracts/:id/periods/:key с
amountKopecks и note: по аренде с коммуналкой каждый месяц своя, а сумма
в договоре остаётся умолчанием. amountKopecks: null снимает уточнение.
Уточнить можно только существующий период — иначе 404.
Пробелы по всем периодическим договорам сразу — GET /api/reports/contract-periods (параметры from, to): месяцы без платежа,
без закрывающего документа или без обоих. Текущий период в отчёт не попадает
— он ещё идёт.
Основание проставляется у платежа (PATCH /api/transactions/:id с
contractId) и у счёта (contractId при создании и обновлении). Договор, по
которому уже есть платежи или счета, не удаляется — его закрывают датой
(endedOn), иначе деньги остались бы без объяснения.
Подбор договора к платежу. Платежу без основания договор проставляется
сам — после загрузки выписки, после сохранения договора и по POST /api/contracts/match (ответ: matched — скольким проставлен, ambiguous —
скольким подходят несколько). Правила по порядку: договор счёта, который
платёж оплатил; договор контрагента с номером из назначения («по договору
№ 12/26 от 01.03.2026»); единственный действующий договор контрагента с
нужной стороны. Контрагент определяется по ИНН платежа. Если назван номер,
которого нет, или подходят несколько договоров, платёж остаётся без
основания, а договоры приходят в contractCandidates строк
GET /api/reports/kudir?year — книга учёта доходов и расходов на УСН за год
(форма по приказу ФНС от 07.11.2023 № ЕА-7-3/816@; year по умолчанию — прошлый):
раздел I по кварталам (quarters[].rows — № п/п, дата, реквизиты документа,
содержание, incomeKopecks без предъявленного НДС, expenseKopecks — только на
«Доходах минус расходы»), итоги за квартал и нарастающим итогом, справка к разделу I
(reference, строки 010–041). Строки — из налогового регистра, итоги совпадают с
оценкой налога. /kudir/pdf — печатная форма, /kudir/xlsx — то же в Excel. На ОСНО —
409. Без права «Зарплаты и ПДн сотрудников» в строках выплат сотрудникам
(вид с ролью salary или получатель — сотрудник) содержание заменяется на
«Выплата сотруднику» или «Поступление от сотрудника»: и в JSON, и в PDF, и в
XLSX. Суммы и итоги те же. С правом книга полная — её и сдают.
GET /api/reports/usn-declaration?year&correction — декларация по УСН за год
(КНД 1152017, приказ ФНС от 26.11.2025 № ЕД-7-3/1017@, формат 5.09): разделы 1.1/1.2
и 2.1.1/2.2 в полных рублях, строки 150–162 (290–320) для ИП, авансы по уведомлениям
закрытых кварталов (closedAdvances), blockers — без чего файл не примут (ИНН, КПП,
ОКТМО, код налоговой, подписант), warnings — что проверить. /usn-declaration/pdf —
показатели для проверки глазами, /usn-declaration/xml — файл
NO_USN_…xml в windows-1251 для личного кабинета или оператора; пока blockers не
пуст — 409 со списком.
GET /api/reports/personified?month=ГГГГ-ММ — персонифицированные сведения о
физических лицах (КНД 1151162, приказ ФНС от 29.09.2022 № ЕД-7-11/878@, формат 5.01):
ежемесячная отчётность в ФНС не позднее 25-го числа (п. 7 ст. 431 НК). Строится из
закрытого месяца зарплаты; в ответе — число сотрудников, итог выплат, blockers (нет
закрытия, пустые СНИЛС/ФИО, реквизиты), warnings и filingNotRequired — не null за
последний месяц квартала: тогда сведения не сдаются (данные входят в РСВ с разделой 3,
письмо ФНС от 28.03.2023 № БС-4-11/3700@), и панель скрывает кнопки отправки,
оставляя только выгрузку XML. /personified/file — файл NO_PERSSVFL_…xml в
windows-1251 для личного кабинета или оператора; СНИЛС и ФИО внутри, поэтому
выгрузка требует права «Сдавать отчётность» и «Зарплаты и ПДн сотрудников», а без
готовых данных — 409 со списком.
GET /api/reports/rsv?year&quarter — расчёт по страховым взносам (КНД 1151111, приказ
ФНС от 29.09.2022 № ЕД-7-11/878@ в редакции от 04.02.2026 № ЕД-1-11/67@, формат 5.08):
квартальная отчётность
до 25-го числа. Строится по закрытым месяцам с начала расчётного периода — с января или, для новой компании, с даты регистрации из устава (CharterCapital; ООО с 03.08.2026 — январь–июль не закрываются), до конца квартала (сводные графы —
нарастающим итогом); в превью — сотрудники, выплаты и взносы, blockers (незакрытые
месяцы, льготный тариф МСП/IT, пустые кадровые поля) и warnings. /rsv/file — файл
NO_RASCHSV_…xml в windows-1251: сводная часть по единому тарифу 01, Раздел 3 с
датами рождения, гражданством (ОКСМ), полом и паспортами — выгрузка под теми же
правами, что и у персонифицированных сведений; без готовых данных 409.
GET /api/reports/ndfl6?year&quarter — расчёт 6-НДФЛ (КНД 1151100, приказ ФНС от
19.09.2023 № ЕД-7-11/649@, формат 5.05, файл NO_NDFL6.2_…): квартальная отчётность
до 25-го числа, Раздел 1 по КБК и Раздел 2 по ставкам шкалы нарастающим итогом
с января (или с даты регистрации — для новой компании, как в RSV) по закрытым
месяцам, шесть сроков перечисления — полосы удержания последнего
квартала из обязательств (нужен график выплат). /ndfl6/file — файл в windows-1251;
квартальные расчёты ПДн не содержат, поэтому право «Зарплаты и ПДн сотрудников» не
требуется; годовой расчёт (с приложением 1 «Персонифицированные сведения») пока не
формируется — 409. Без готовых данных — тоже 409 со списком.
GET /api/reports/efs1?year&quarter — ЕФС-1, раздел 2 «Взносы на травматизм» в СФР
(приказы СФР от 17.11.2025 № 1462/1463): квартально до 25-го числа. Численности и
суммы (выплаты, база, исчислено) — по ТипСуммыЗаПериоды: с начала года (или с даты
регистрации — для новой компании), на конец предыдущего периода и три последних
месяца календарного квартала — месяц до регистрации приходит с нулём; закрываются
месяцы с даты регистрации, среднесписочная — среднее по ним же (правила Росстата
для созданных в течение года). Тариф из налогового
профиля. Нужен регистрационный номер СФР в настройках. /efs1/file — UTF-8-файл;
в разделе нет ПДн, поэтому достаточно права «Сдавать отчётность». Блок результатов
СОУТ (РПО) по умолчанию — все места 1 класса, панель предупреждает.
GET /api/reports/missing-docs. У платежа contractSource: auto —
подобрано, manual — указано через PATCH (в том числе contractId: null);
указанное вручную подбор не переписывает.
Документ договора. Скан, ссылка в ЭДО и подпись договора — документ
библиотеки вида contract, на который указывает documentId договора.
POST /api/contracts/:id/document заводит его, если его нет, и возвращает.
Номер, дата, контрагент и сумма у договора и документа общие: правка с любой
стороны переносится на другую. У такого документа в ответах
/api/documents заполнен contractId. Документ вида contract, заведённый
в библиотеке без связи, находит договор с тем же контрагентом, номером и
датой, а если такого нет — заводит карточку.
Снимок условий оферты. Страницу оферты поставщик меняет молча, поэтому
сервер сохраняет её на дату акцепта: POST /api/contracts/:id/snapshot — по
кнопке, и сам — при первой оплате, привязанной к оферте (подбор договора или
PATCH /api/transactions/:id с contractId), фоном: оплата не ждёт чужой сайт
и не падает из-за него. Снимок — документ библиотеки вида offer_snapshot
(раньше — other с меткой «Снимок оферты»; такие переводятся миграцией при
старте), связью с договором (и с платежом у автоснимка) и PDF-файлом:
адрес, итоговый адрес после перенаправлений, время получения, HTTP-статус,
SHA-256 ответа и текст страницы; исходный HTML с <base href> вложен в PDF
файлом offer.html. Снимок только у оферты со ссылкой на условия (kind не
contract, offerUrl задан) — иначе 400. Сервер ходит по адресу так же
осторожно, как по вебхуку: в проде только https и никаких внутренних адресов,
каждое перенаправление проверяется заново; таймаут 10 с, не больше 5 МБ;
скрипты не выполняются, картинки и стили не запрашиваются. Неудача — 400
(адрес), 413 (больше 5 МБ), 415 (по ссылке не страница), 502 (страница
не открылась); в теле кроме error — тот же ответ, что у GET.
GET /api/contracts/:id/events → ContractEventDto[], свежие сверху (до
500): журнал договора — at, type, actorName (человек или «Saby»),
auto, fields (у updated — какие поля поменялись: number, date,
counterpartyId, subject…), documentId, documentLabel, note и text
— запись простым языком. Типы: created (заведён, в том числе из документа
ЭДО), updated (правка, только реально изменённые поля), edo_merged /
edo_split (объединён с документом из ЭДО вручную или автоматически /
разъединён), edo_state (сменился статус ЭДО «Самого договора» —
синхронизация), document_linked / document_unlinked (связь документа с
договором), archived / unarchived. Журнал пишет сервер, изменить его
нельзя.
GET /api/contracts/:id/snapshots → {offerUrl, snapshots, log}: снимки
(документы библиотеки, свежие сверху) и журнал попыток — последние 20, свежие
сверху: at, trigger (manual — кнопкой, payment — при оплате), ok,
error, documentId, transactionId. Неудачный автоснимок виден только
здесь и в логе сервера; следующая оплата попробует снова, после удачного
автоснимка — нет.
GET /api/contracts/:id/related — всё, что связано с договором, для вкладок
карточки одним запросом: invoices (счета с contractId: номер, дата,
статус, сумма, оплачено), payments (платежи с contractId), totals
(invoicedKopecks — выставлено без черновиков и аннулированных,
paidKopecks — оплачено по ним, debtKopecks — остаток), sales (наши
документы продажи по договору, с documentId экземпляра), incoming
(входящие закрывающие документы со связью с договором), letters —
переписка: via: "subject" — договор указан в письме, "document" — письмом
пришёл или ушёл документ договора, "counterparty" — прочие письма
контрагента (до 50, в конце списка). counts — числа на вкладках: money
(счета и платежи), closing (документы продажи и входящие закрывающие),
letters (без прочих писем контрагента).
GET /api/contracts/:id/tree → {contractDocument, appendices, annexes, snapshots, other, count}: документы, чей дом — этот договор
(owner = contract:<id>), в порядке чтения. contractDocument — сам договор
(Contract.documentId, может быть null); appendices — приложения и
спецификации по номеру (числа внутри номера сравниваются как числа: 1, 2, 10);
annexes — допсоглашения по дате, затем по номеру; snapshots — снимки
оферты, свежие сверху; other — прочее по дате. Узел — {document, children}:
children — вложения документа (parentId), например приложение к
допсоглашению. Вложение, чей родитель пропал, показывается в корне. count —
сколько документов в доме, кроме самого договора. Документы, только
связанные с договором (акты, счета-фактуры), в дерево не входят — они в
GET /api/documents?link=contract:<id>.
Контрагенты — /api/counterparties
Единственный модуль, кроме счетов, доступный по ключу приложения: контрагента надо завести, прежде чем выставить ему счёт.
Реквизиты проверяются по типу (kind): у юрлица ИНН — 10 цифр, ОГРН — 13; у ИП ИНН — 12 цифр, ОГРНИП — 15, КПП нет; у физлица нет ни КПП, ни ОГРН. Контрольные суммы ИНН и ОГРН — по алгоритмам ФНС, буквы КПП приводятся к заглавным. Пустое поле — не ошибка. Ошибка — 400 с error и ошибками по полям в fields: {"error": "…", "fields": {"inn": "…"}}. При правке проверяются только переданные поля; смена типа на ИП или физлицо убирает КПП (и ОГРН у физлица).
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/counterparties |
ключ приложения, скоуп counterparties:read |
POST |
/api/counterparties |
ключ приложения, скоуп counterparties:write |
DELETE |
/api/counterparties/:id |
ключ приложения, скоуп counterparties:write; человеку ещё раздел — управление |
POST |
/api/counterparties/:id/archive |
ключ приложения, скоуп counterparties:write |
PATCH |
/api/counterparties/:id |
ключ приложения, скоуп counterparties:write |
Библиотека документов — /api/documents
Карточка документа: вид, номер, дата, контрагент, файлы и связи. Документ —
не файл: бумажный оригинал, скан и документ в ЭДО описываются одной карточкой
(source: paper, file, edo).
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/documents |
JWT; раздел «Документы» — просмотр |
GET |
/api/documents/counts |
JWT; раздел «Документы» — просмотр |
POST |
/api/documents |
JWT; раздел «Документы» — изменение |
GET |
/api/documents/:id |
JWT; раздел «Документы» — просмотр |
PATCH |
/api/documents/:id |
JWT; раздел «Документы» — изменение |
DELETE |
/api/documents/:id |
JWT; раздел «Документы» — управление |
POST |
/api/documents/:id/files |
JWT; раздел «Документы» — изменение |
DELETE |
/api/documents/:id/files/:fileId |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/links |
JWT; раздел «Документы» — изменение |
DELETE |
/api/documents/:id/links/:type/:refId |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/original |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/recognize |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/recognize |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/ocr/retry |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/edo/copy |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/archive |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/unarchive |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/keep |
JWT; раздел «Документы» — изменение |
POST |
/api/documents/:id/merge-into-contract |
JWT; разделы «Документы» и «Договоры» — изменение |
POST |
/api/documents/:id/create-contract |
JWT; разделы «Документы» и «Договоры» — изменение |
POST |
/api/documents/:id/split-edo |
JWT; разделы «Документы» и «Договоры» — изменение |
GET |
/api/documents/people |
JWT; раздел «Документы» — просмотр; право «Конфиденциальные документы» |
GET |
/api/documents/:id/access-log |
JWT; раздел «Документы» — просмотр; право «Конфиденциальные документы» или «Управление пользователями» |
Конфиденциальные документы (docs/research/access-policy.md §6.4). Поле
confidential — документ видят держатели права «Конфиденциальные документы»
(docs.confidential) и назначенные люди (accessUserIds — id пользователей
компании, список кандидатов — GET /api/documents/people). Остальным такого
документа нет нигде: ни в списке и поиске (в том числе по тексту скана), ни в
счётчиках, ни в дереве и связях договора, ни в связях других документов; по
id — 404, как у несуществующего; его файлы по /api/files/:id* — тоже
404. Если отметку не передать, она ставится по умолчанию: закрыты кадровые
(kind: hr) и документы с домом у сотрудника; тот же документ, ставший
кадровым при правке, закрывается сам. Менять отметку и назначенных может
только держатель права (иначе 403 с code: access_denied); назначенные
приходят в ответе только ему, остальным — []. Человек без права, заведший
закрытый документ, становится назначенным — иначе документ пропал бы у него
из рук. Назначить можно только члена компании (404).
Журнал доступа (§6.5). Каждое открытие карточки конфиденциального
документа (GET /api/documents/:id) и его файла (GET /api/files/:id/content)
записывается до ответа: { at, lastAt, repeats, userId, userName, action: view|download, documentId, documentTitle, fileId, fileName }. Повторы одного
человека по тому же документу, файлу и действию в пределах 10 минут
склеиваются (repeats, lastAt). Записи хранятся 3 года и не правятся.
GET /api/documents/:id/access-log?cursor&limit — журнал документа страницами
{ items, nextCursor }, свежие сверху; общий журнал компании —
GET /api/access-log.
Фильтры списка: kind (через запятую), direction, source, counterpartyId,
tag, from, to (даты в виде ГГГГ-ММ-ДД), q (номер, название, описание,
контрагент), link в виде <тип>:<id>, awaiting=1 (ждут подписи),
payment (unpaid, partial, paid — входящие закрывающие документы с
суммой по оплате в журнале; «документ без оплаты» — payment=unpaid) и
limit (до 500). Входящие закрывающие документы с суммой — регистр расходов:
поля vatKopecks (не больше суммы, иначе 400), expenseAccount и payment
описаны в разделе «Бухгалтерия».
Папка «Разобрать» и архив. Карточка, которая, похоже, не нужна, попадает в
«Разобрать» сама (вычисляется фильтром, без фоновой задачи), и в карточке
видно, почему: triageReasons — annulled («Аннулирован в ЭДО»), rejected
(«Отклонён в ЭДО»), duplicate (подпись дубликата — triageDuplicateLabel:
«Дубль документа № … от …»), fetch_failed («Файл не скачался», в том числе
карточка «не удалось разобрать» из ЭДО) или stale_draft («Черновик без
данных больше 30 дней»). Решение человека — отметкой, не правкой карточки:
POST /api/documents/:id/keep («Оставить» — документ больше не предлагается;
новый переход ЭДО в аннулирован/отклонён снова его предложит) и
POST /api/documents/:id/archive («В архив» — обратимо:
POST /api/documents/:id/unarchive; не в архиве — 409). Пока решение не
принято, документ ждёт в списке GET /api/documents?triage=1, счётчик —
triage в ответе /api/documents/counts; убранные — в ?archived=1,
счётчик archived. Обычный список (GET /api/documents), вкладки и счётчики,
поиск, «Проверить» и «Чего не хватает» идут без архивных и без нерешённых из
«Разобрать»; учёта, налогов, книг НДС, закрытия периодов, договоров и сверки
они не двигают: архивный и нерешённый — до решения, аннулированный и
отклонённый в ЭДО — никогда, даже после «Оставить». Удаление — как раньше,
вручную, без автоматизма.
Договоры из ЭДО. Карточка вида «Договор» из ЭДО (source: edo, без своего
договора) — не сам договор «Потока», а его носитель в системе ЭДО; её можно
объединить с уже заведённым договором или завести по ней договор.
POST /api/documents/:id/merge-into-contract {contractId, confirm?, dryRun?}
— файлы (PDF, XML, подписи) и ЭДО-поля (edo.*, включая packageNumber —
номер пакета Saby для справки) переезжают в документ «Сам договор» выбранного
договора (он становится source: edo), карточка удаляется, а ключ вложения
(system, externalId, attachmentId) остаётся на «Самом договоре»:
следующее событие ЭДО не заводит карточку заново, а обновляет статус договора.
Другие вложения того же документа Saby (пакет «договор + приложение»)
присоединяются тем же объединением: их ключи — в edo.mergedAttachmentIds,
отдельных карточек по ним синхронизация не заводит. Ссылки на карточку
(вложения parentId, связи document, edo.duplicateDocumentId) переходят
на «Сам договор». Реквизиты договора (номер, дата, стороны, сумма) не
меняются. dryRun: true → 200 {changes, warnings}: что изменится и о чём
предупредить, без изменений. Предупреждения (warnings: [{code, text}]):
other_counterparty (ИНН документа и договора разные), archived_contract
(договор в архиве), annulled (документ аннулирован или отклонён в ЭДО),
replace_edo (у «Самого договора» уже есть объединённый документ ЭДО: он
снова станет отдельной карточкой — аннулированный сразу в архиве, — а его
место займёт этот), confidential_card (закрытая карточка закроет и «Сам
договор»: доступ — её назначенным людям). Есть предупреждения и нет
confirm: true — 409 {error, warnings, needsConfirm: true}. Закрытый «Сам
договор», которого человек не видит, — 403 без реквизитов; закрытая
карточка при открытом «Самом договоре» — только с правом docs.confidential
(иначе 403). «Сам договор» — сам документ из ЭДО (договор заведён по
карточке, снимка нет) — 409: подменять его нельзя. Ответ — документ «Сам
договор» с edoMerge: {at, byName, auto, cards}.
POST /api/documents/:id/split-edo («Разъединить», :id — «Сам договор») →
{document, restoredIds}: карточки из ЭДО восстанавливаются с теми же id,
своими файлами и ЭДО-полями (статус — нынешний), «Сам договор» — как до
первого объединения (источник, ЭДО-поля, подпись, файлы, связи, закрытость),
ссылки возвращаются. Разъединённую карточку (edo.splitAt)
автосопоставление больше не трогает. Не объединялся — 409.
ЭДО-поля документа, связанного с вложением Saby (edo.attachmentId), меняют
только синхронизация, объединение и разъединение: PATCH /documents/:id с
source/edo их не трогает (у ручной ссылки ЭДО — как прежде).
POST /api/documents/:id/create-contract {side?, counterpartyId?, number?, date?, confirm?} — заводит договор из карточки: номер — из документа (номер
пакета Saby номером договора не становится — пусто, если человек не передал
number), дата — из имени файла «от ДД.ММ.ГГГГ» или карточки, сторона по
направлению (входящий — «мы покупатель», можно передать side), контрагент
по ИНН из ЭДО (нет карточки контрагента — заводится по ИНН и названию, 12
цифр — ИП; несколько карточек с этим ИНН — 400 с fields.counterpartyId,
выбрать явно); карточка становится «Самим договором» (дом и связь договора,
contractId в ответе), без копий. Предупреждения has_candidates (похожий
договор уже заведён) и annulled — 409 {warnings} без confirm: true.
Синхронизация Saby при заведении карточки-договора сама объединяет её,
только когда уверенность высокая: номер взят из документа (XML или имя
файла, edo.numberSource: xml/file; package — номер пакета, ему не
верим), он сильный (не короче четырёх значимых знаков, не «б/н», не дата),
вложение — сам договор (не «Дополнительное соглашение», «Приложение»,
«Спецификация», «Акт» по имени файла), ровно один договор «Потока» с тем же
ИНН контрагента и совпадающим номером (сравнение нормализует №, пробелы,
_ / - \), он не в архиве и у его «Самого договора» ещё нет документа ЭДО.
Иначе карточка остаётся с подсказкой edo.contractHint «Похоже на договор
№ … — объединить?» (у «Самого договора» аннулированный документ — «заменить
им прежний документ ЭДО?»); подсказка снимается, когда кандидатов не стало.
Аннулированные и отклонённые в ЭДО не объединяются автоматически никогда.
У входящего УПД (kind: upd) и счёта-фактуры (kind: vat_invoice) есть
purchaseInvoice — реестр для книги покупок. У остальных документов и пока
блок не заполнен поле null; null в PATCH снимает документ с реестра.
Номер (number) хранится как напечатан, без своей нормализации. Дальше:
date, sellerInn, sellerKpp (обязателен при ИНН из 10 цифр и пуст при
ИНН из 12), sellerName, receivedOn, acceptedOn (дата принятия на учёт,
графа 8), rates — строки {rate, baseKopecks, vatKopecks} по ставкам 22,
20, 10, 7, 5 и 0. НДС строки равен базе × ставка (допуск 1 коп.; у 0% налог
строго 0). Если строки есть, их сумма и НДС сходятся с amountKopecks и
vatKopecks. updStatus — 1 или 2 у УПД и null у отдельного
счёта-фактуры; статус 2 в книгу покупок не ставится. operationCode — 01,
02 или 18. deductionQuarter — ГГГГ-Qn или пусто (тогда квартал
acceptedOn): не раньше квартала принятия и не позже квартала даты «плюс
три года» (п. 1.1 ст. 172 НК), один квартал на документ. Пустой квартал
обязателен, если forTaxableOperations равен false (по умолчанию true,
п. 2 ст. 171 НК) или статус УПД — 2. Исправление — пара
correctionNumber и correctionDate (строка 1а). Корректировочный — пара
ksfNumber и ksfDate плюс исходный originalNumber и originalDate
(строка 1б). Чужой вид, направление или несходящиеся суммы — 400 с
fields. Оплата поставщику — прежняя связь transaction, отдельного поля
нет. Код 02 (аванс поставщику) в реестре хранится, в книгу покупок этой
задачей не проводится.
Виды (kind) разложены по группам (DOCUMENT_KIND_GROUPS в
packages/shared): договорные — contract, annex, appendix
(приложение), specification, offer_snapshot (снимок условий оферты,
делает сервер); первичные — act, waybill, upd, vat_invoice,
invoice, receipt, reconciliation (акт сверки); внутренние —
power_of_attorney, order, statute, decision (решение или протокол
участников), hr; переписка — letter; прочее — other. Фильтр group
(contractual, primary, internal, correspondence, other) отбирает
виды группы.
Дом документа — owner: {type, id, label} | null: объект, где документ живёт
(contract, sales_document, letter, employee, counterparty). Дом —
одна из связей, главная: при создании или правке owner: {type, id} связь
того же типа появляется сама; убрали связь дома — дома больше нет. Не указан
при создании — сервер ставит его по виду и единственной связи: допсоглашение,
приложение, спецификация, снимок оферты и сам договор с одним договором —
в договоре; письмо с одним письмом — в письме; кадровый с одним сотрудником —
у сотрудника. owner: null — без дома (внутренние документы компании и
входящие первичные). Объекта дома нет в вашей компании — 404.
ownerRole — main (сам договор или экземпляр документа продажи),
attachment (вложение дома) или null. salesDocumentId — у экземпляра
нашего документа продажи. parentId — родитель в том же доме (приложение к
допсоглашению): вложенность в один уровень, родитель из другого дома, сам
вложенный или сам документ — 400. Удалили родителя или он ушёл из дома —
вложения остаются в доме, в корне. Фильтр owner=<тип>:<id> — документы
дома.
Вкладки архива — view: incoming (входящие первичные: акт, накладная,
УПД, счёт-фактура, счёт, чек — регистр расходов), outgoing (те же виды,
исходящие), contractual (договорные и акты сверки), internal
(внутренние), reporting (платёжные поручения, декларации и отчёты,
квитанции налоговой, требования), all. Вкладка задаёт выборку, прочие
фильтры её сужают, но не расширяют: view=incoming&kind=contract пуст;
неизвестная вкладка — 400 с перечнем допустимых. unhomed=1 — документы
без дома и без единой связи, которые не являются документом договора. В
outgoing не попадают экземпляры документов продажи — сами документы продажи
отдаёт /api/sales-documents.
С view или cursor ответ — страница архива { items, nextCursor, total }:
limit — от 1 до 200, по умолчанию 50; total — сколько документов под
фильтром всего; следующая страница — тот же запрос с cursor=<nextCursor>,
nextCursor: null — страница последняя. Порядок — по дате и id убыванием,
курсор — позиция последней строки, поэтому документ, заведённый между
страницами, не сдвигает следующую. Испорченный курсор — 400. Без view и
cursor ответ — массив, как раньше (limit до 500).
GET /api/documents/counts?from&to — счётчики вкладок за период:
incoming, outgoing, contractual, internal, reporting, all — по
{ total, attention }, где attention — сколько требует действия (входящие
без суммы или оплаченные не полностью; исходящие и договорные, которые ждут
подписи; у исходящих в счёт идут и документы продажи, кроме аннулированных;
у отчётности — требования налоговой с их сроком ответа);
control — { awaiting, unhomed, recognized, missing, noOriginal, overdueOriginals, attention }: документы, которые ждут подписи, непривязанные,
распознанные из файлов, но не подтверждённые человеком (считаются по всему
архиву: платёжка или декларация на «Входящих» не числятся, а проверять их
надо), расходы без закрывающего документа (как withoutDocument.count в
GET /api/reports/missing-docs), где оригинал не отмечено и выданные, которые
не вернули в срок. attention — бейдж вкладки: число РАЗНЫХ документов,
попавших хоть в одну проверку (одна карточка бывает в нескольких сразу, и
сумма счётчиков считала бы её несколько раз), плюс расходы без документа.
Рядом triage — сколько ждёт решения в «Разобрать», archived — сколько
убрано в архив; в «Контроль» они не суммируются.
Отметка о подписи exchange — та же, что у документов продажи: mode
(edo, paper, none), sentAt, signedAt, url, note. По умолчанию
документ из ЭДО подписывается в ЭДО, бумажный договор, доп. соглашение,
приложение, спецификация, акт, накладная, УПД и акт сверки — живой подписью,
остальным подпись не нужна.
Связь (links) — с счётом (invoice), платежом (transaction), сотрудником
(employee), проектом (project), письмом журнала (letter), закрытием периода (period — квитанция или
протокол приёма отчётности), обязательством по налогу (obligation — налоговая
платёжка с конкретным КБК, привязывает человек в карточке по подсказке) или другим документом (document). Подпись
связи проставляется сервером: объект должен существовать в вашей компании,
иначе 404. Связь с обязательством информационная: суммы ЕНС всегда считаются
по выписке, платёжка их не меняет.
Файл прикладывается уже загруженным — сначала POST /api/files, потом
POST /api/documents/:id/files с { "fileId": "..." }. Отвязанный файл, если
он больше не лежит ни в одном документе, удаляется вместе с содержимым;
удалить его напрямую, пока он приложен, нельзя — 409.
Где оригинал. original документа — null (не отмечено) или
{state, locationId, locationPath, note, holderName, employeeId, dueDate, letterId, letterLabel, url, label, overdue, changedAt, changedByName}.
state: office (у нас — место хранения и примечание «файл 12»),
counterparty (у контрагента), sent (отправлен, можно указать письмо
журнала letterId), scan_only (только скан), issued (выдан сотруднику —
holderName или employeeId обязательны, dueDate — вернуть до), edo
(электронный, в ЭДО — url, по умолчанию ссылка документа). label — «где»
одной строкой: «Офис / Шкаф 1 / П-3, файл 12», «выдан Иванову до
01.10.2026», «в ЭДО»; overdue — выдан и не возвращён в срок. Документу из
ЭДО состояние edo ставится само. POST /api/documents/:id/original с
{state, …} (или {state: null} — снять отметку) меняет состояние, прежнее
уходит в originalHistory (свежие сверху, приходит в карточке документа).
Место, сотрудник или письмо не из вашей компании — 400. Фильтры списка:
noOriginal=1 — где оригинал не отмечено (кроме документов из ЭДО),
overdue=1 — выданные и не возвращённые в срок, location=<id> — лежат в
месте или любом вложенном в него. Выданный и не возвращённый к сроку оригинал
— письмо администраторам компании, одно на выдачу (задача
original-reminders, с 9 утра по времени компании).
Распознавание и поиск по тексту. Каждый загруженный файл встаёт в
очередь распознавания. У PDF текстовый слой снимается сразу (страница короче
32 знаков считается сканом); сканы и фото распознаёт отдельный воркер
Tesseract 5 (rus+eng), не больше 50 страниц на файл. Облачного распознавания
нет — файлы не покидают сервер. ocrStatus документа — по его файлам:
pending (в очереди), processing (распознаётся), done (текст в поиске),
failed (не получилось за три попытки: пауза 5 мин, 1 ч, 6 ч), skipped
(распознаны не все страницы), null — файлов нет. POST /api/documents/:id/ocr/retry возвращает в очередь файлы документа в
failed или skipped — попытки с нуля; повторять нечего — 409.
Распознавание карточки из файла. По тексту файла (слой или OCR)
POST /api/documents/recognize с { "fileId": "…" } и
POST /api/documents/:id/recognize определяют вид документа (счёт-фактура,
УПД статуса 1/2, корректировочный счёт-фактура, акт, ТОРГ-12, счёт на оплату,
а также платёжное поручение — в том числе налоговое с полями 101–110, кассовый
чек ККТ по 54-ФЗ, налоговая и иная отчётность по КНД, квитанции и извещения
налоговой, требования и решения о блокировке счёта) и возвращают
{ fileId, status, ocrStatus, result, suggestions }. status:
recognized — разбор готов; no_text — текст есть, но знакомой формы нет;
pending/processing — текст ещё распознаётся (веб ждёт и спрашивает снова);
failed/skipped — очередь не справилась. result — поля с уверенностью
0..100 (number, date, sellerName, sellerInn, sellerKpp, buyerInn,
buyerKpp, суммы, строки позиций, ставки, строки 5 и 5а), самопроверка
арифметики и предупреждения («документ выставлен не вам», суммы не сходятся).
У платёжек, чеков и отчётности в result приходят ещё knd (форма по КНД),
periodKey (отчётный период «2026-Q3» или год), correctionNumber,
dueDate (срок ответа требования) и extras — подписанные поля вне карточки
(КБК, ОКТМО, УИН, поля 101–110 платёжки, ФД/ФН/ФП чека, результат квитанции);
каждое со своей уверенностью. Черновик распознанного не участвует в учёте:
новые виды не входят в закрывающие, проводок по ним нет до подтверждения.
suggestions — подсказки по данным компании: counterpartyId (контрагент с
ИНН продавца или получателя платёжки; нет — counterpartyCreateByInn: true,
реквизиты предложит GET /api/requisites/by-inn), payment — исходящий
платёж тому же поставщику на ту же сумму в окне ±60 дней (для платёжки —
операция выписки в окне ±3 дня, по ИНН получателя и номеру п/п), ещё не
привязанный к другому документу, expenseAccount — статья по прошлым
документам поставщика или по учётной политике; для отчётности — period
(закрытый налоговый период тем же ключом) и для квитанций — report
(единственная загруженная декларация за два месяца). У налоговой платёжки
(есть КБК) контрагент по ИНН казначейства не предлагается, а в tax приходит
подсказка по КБК: ЕНП (ens: true) — ensCoverage с тем, что пополнение
покроет по расчёту ЕНС (covers — ближайшие обязательства; если операция
выписки с платёжкой уже есть, alreadyInLedger: true — расчёт её уже
учитывает); конкретный КБК — candidates (обязательства того же вида, сначала
период из поля 107, у каждого match: exact/under/over; кандидатов
несколько — выбор за человеком, автопривязки нет); пени и штраф — только
note («обязательство не закрывают»); плательщик чужой (notOurPayer) —
подсказки привязки нет. Привязка ставится человеком:
POST /api/documents/:id/links с {"type": "obligation", "id": "<обязательство>"}.
Карточка, созданная
по результату, создаётся с recognitionDraft: true и числится в списке
«Проверить» (recognized=1 в GET /api/documents, счётчик
control.recognized в /counts); правка карточки с recognitionConfirmed: true в PATCH /api/documents/:id снимает признак.
Поиск q — полнотекстовый по карточке (вид, номер, название, описание,
метки, контрагент, сумма в рублях) и тексту файлов с русской морфологией
(«договора» находит «Договор»), плюс прежнее совпадение подстроки в карточке
(номер «12-А»). Результаты — одна страница до 200 документов по
релевантности, без курсора (nextCursor: null); фильтры вкладки действуют
поверх поиска. У каждого найденного — match: {snippet, source, page, fileName}: ±80 знаков вокруг совпадения, source — card или file,
page — номер страницы файла. Искать можно только среди своих документов:
текст чужой компании не находится.
POST /api/documents/:id/edo/copy скачивает документ по ссылке ЭДО и
сохраняет копию у себя (edo.copyFileId): ссылка живёт, пока жив договор с
оператором, а документы нужны все пять лет хранения. Если по ссылке приходит
страница входа, а не документ, — 415 с просьбой приложить файл вручную;
409 — копия уже сохранена.
Места хранения оригиналов — /api/document-locations
Помещение → шкаф → полка → папка: уровни и названия свободные.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/document-locations |
JWT; раздел «Документы» — просмотр |
POST |
/api/document-locations |
JWT; раздел «Документы» — изменение |
PATCH |
/api/document-locations/:id |
JWT; раздел «Документы» — изменение |
DELETE |
/api/document-locations/:id |
JWT; раздел «Документы» — управление |
GET — все места плоским списком по пути: {id, name, parentId, note, path, documents, total}, где documents — документов прямо в месте, total — с
вложенными. POST — {name, parentId?, note?}. PATCH — {name?, parentId?, note?}: смена parentId переносит место со всем содержимым (документы
ссылаются на место и переезжают вместе с ним); внутрь самого себя — 400.
DELETE — только пустое место, иначе 409. Что лежит в месте —
GET /api/documents?location=<id>.
Сотрудники — /api/employees
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/employees |
JWT; раздел «Сотрудники» — просмотр |
GET |
/api/employees/options |
JWT; раздел «Сотрудники» — просмотр |
POST |
/api/employees |
JWT; раздел «Сотрудники» — изменение, право «Зарплаты и ПДн сотрудников» |
DELETE |
/api/employees/:id |
JWT; раздел «Сотрудники» — управление |
POST |
/api/employees/:id/archive |
JWT; раздел «Сотрудники» — изменение |
PATCH |
/api/employees/:id |
JWT; раздел «Сотрудники» — изменение |
POST |
/api/employees/:id/bonuses |
JWT; раздел «Сотрудники» — изменение, право «Зарплаты и ПДн сотрудников» |
DELETE |
/api/employees/:id/bonuses/:bonusId |
JWT; раздел «Сотрудники» — управление, право «Зарплаты и ПДн сотрудников» |
ПДн сотрудников (право hr.pii, access-policy.md §6.2). Без права
сотрудник отдаётся без полей EMPLOYEE_PII_FIELDS из @ooo/shared —
rateMilli, salaryFromPosition, employmentBasis, salaryKopecks,
monthlyBonusKopecks, bonuses, startedOn, endedOn — и с pii: false:
{id, name, position, positionId, isHead, active, pii}. Поля не пустые, а
отсутствуют. С правом — всё и pii: true. В PATCH без права можно менять
имя, должность и active; поле из списка ПДн в теле — 403 с кодом
access_denied. Принять сотрудника (POST) и менять премии без права
нельзя: в них оклад.
GET /api/employees/options — список для выбора (привязка документа,
получатель): [{id, name, position, active}], без ПДн, одинаковый для всех,
кому открыт раздел.
Файлы компании — /api/files
Сканы документов, печать и подпись, копии документов из ЭДО. Файл лежит в S3-совместимом хранилище, а ссылок наружу не выдаётся: содержимое отдаёт сам API, проверив сессию и компанию.
Загрузка — multipart/form-data, поле file. Тип определяется по
содержимому, а не по расширению: PDF, JPEG, PNG, TIFF. Предел одного файла —
25 МБ, место на компанию — 5 ГБ по умолчанию (меняется оператором).
| Метод | Путь | Доступ |
|---|---|---|
POST |
/api/files |
JWT; раздел «Документы» — изменение |
GET |
/api/files/usage |
JWT; раздел «Документы» — просмотр |
GET |
/api/files/:id |
JWT; раздел «Документы» — просмотр |
GET |
/api/files/:id/content |
JWT; раздел «Документы» — просмотр |
DELETE |
/api/files/:id |
JWT; раздел «Документы» — управление |
GET /api/files/:id/content отдаёт сам файл; с ?download=1 — вложением
(Content-Disposition: attachment). Повторная загрузка того же содержимого
отвечает 200 и прежним файлом вместо 201: место оно занимает один раз.
Отдельные коды: 413 — файл больше предела, 415 — такой тип не принимаем,
507 — место компании закончилось, 503 — хранилище не настроено.
Файл, лежащий в конфиденциальном документе, виден тому, кому виден хоть
один документ, где он лежит; остальным /api/files/:id, /content и
DELETE отвечают 404. Такой файл отдаётся с Cache-Control: private, no-store, и каждое открытие пишется в журнал доступа. Повторная загрузка
того же содержимого человеком, которому прежний файл не виден, заводит
отдельный файл (201), а не отдаёт скрытый.
Серии счетов — /api/invoice-series
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/invoice-series |
JWT; раздел «Справочники» — просмотр |
POST |
/api/invoice-series |
JWT; раздел «Справочники» — изменение |
DELETE |
/api/invoice-series/:id |
JWT; раздел «Справочники» — управление |
POST |
/api/invoice-series/:id/archive |
JWT; раздел «Справочники» — изменение |
PATCH |
/api/invoice-series/:id |
JWT; раздел «Справочники» — изменение |
Счета на оплату — /api/invoices
Основной модуль. Доступен по ключу приложения со скоупами invoices:read / invoices:write.
send-email отправляет выставленный счёт письмом с PDF во вложении: тело { to, subject?, message? }, to — строка через запятую или массив, до 5 адресов. Без темы и текста уходит письмо по шаблону. Письмо идёт с адреса «Потока» от имени компании, ответ — на почту компании из настроек. Черновик и аннулированный счёт — 409, почта не настроена — 503, почтовый сервер отказал — 502. Каждая отправка пишется в журнал счёта.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/invoices |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices |
ключ приложения, скоуп invoices:write |
DELETE |
/api/invoices/:id |
ключ приложения, скоуп invoices:write; человеку ещё раздел — управление |
GET |
/api/invoices/:id |
ключ приложения, скоуп invoices:read |
PATCH |
/api/invoices/:id |
ключ приложения, скоуп invoices:write |
GET |
/api/invoices/:id/bank-status |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices/:id/cancel |
ключ приложения, скоуп invoices:write; человеку ещё право «Выставлять счета и документы», раздел — управление |
GET |
/api/invoices/:id/documents |
ключ приложения, скоуп invoices:read |
GET |
/api/invoices/:id/history |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices/:id/issue |
ключ приложения, скоуп invoices:write; человеку ещё право «Выставлять счета и документы» |
GET |
/api/invoices/:id/payment-status |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices/:id/payments |
ключ приложения, скоуп invoices:write |
DELETE |
/api/invoices/:id/payments/:transactionId |
ключ приложения, скоуп invoices:write |
GET |
/api/invoices/:id/pdf |
ключ приложения, скоуп invoices:read |
GET |
/api/invoices/:id/print |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices/:id/send-email |
ключ приложения, скоуп invoices:write; человеку ещё право «Отправлять письма» |
POST |
/api/invoices/:id/send-to-bank |
ключ приложения, скоуп invoices:write; человеку ещё право «Выставлять счета и документы» |
GET |
/api/invoices/payments/unmatched |
ключ приложения, скоуп invoices:read |
POST |
/api/invoices/reconcile |
ключ приложения, скоуп invoices:write |
Журнал доступа — /api/access-log
Кто и когда открывал конфиденциальные документы и их файлы — по всей
компании (docs/research/access-policy.md §6.5). Записи те же, что у
GET /api/documents/:id/access-log; из API журнал не правится и не удаляется.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/access-log |
JWT; право «Управление пользователями» |
Фильтры: userId, documentId, from, to (ГГГГ-ММ-ДД), страницы —
cursor и limit (до 200). Ответ — { items, nextCursor }, свежие сверху.
Неверный id, дата или курсор — 400.
Приглашения — /api/invites
Сотрудника приглашают по одноразовой ссылке: придёт письмо, человек сам задаст пароль — администратор его не знает. Ссылка действует 7 дней и срабатывает один раз; повторная отправка (resend) выдаёт новую ссылку, старая перестаёт работать; новое приглашение на ту же почту отзывает прежнее. Если письмо не ушло (почта не настроена), ответ содержит link — передать её самому; иначе link — null.
По ссылке (/accept/:token, без входа): GET — компания, почта, роль и есть ли уже учётная запись; POST — принять. Тело всегда содержит pdConsent: true — отдельное согласие на обработку ПДн (ст. 9 152-ФЗ). Без него 400, ссылка остаётся. Сервер пишет дату, версию политики, IP и User-Agent. Если согласия на учётке ещё не было, заполняются поля pdConsent*; повторная отметка дописывается в legalAcceptances (согласие и политика) и не затирает первую дату. Оферту приглашённый не акцептует. Новый человек передаёт ещё name и password и сразу получает сессию. Если учётная запись с этой почтой уже есть, принять можно только войдя в неё (Authorization: Bearer) — компания добавится, пароль не меняется. Использованная, истёкшая или отозванная ссылка — 410.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/invites |
JWT; право «Управление пользователями» |
POST |
/api/invites |
JWT; право «Управление пользователями» |
POST |
/api/invites/:id/resend |
JWT; право «Управление пользователями» |
DELETE |
/api/invites/:id |
JWT; право «Управление пользователями» |
GET |
/api/invites/accept/:token |
без авторизации (токен из ссылки) |
POST |
/api/invites/accept/:token |
без авторизации; для существующей учётной записи — её JWT |
Заявки с лендинга — /api/leads
POST публичный — его дёргает форма заявки. Тело: { email, inn, source?, termsAccepted, pdConsent }. Обе отметки — только true, как при регистрации: termsAccepted — Оферта и Условия использования, pdConsent — согласие на обработку ПДн (ст. 9 152-ФЗ); иначе 400, заявка не пишется. При успехе сохраняются дата согласия, версия политики, IP и User-Agent, а в legalAcceptances — редакции всех четырёх документов. Ограничение: 20 заявок с одного IP в сутки; повтор с тем же email обновляет заявку (и согласие), а не плодит вторую. Всё остальное — только оператору платформы: это его воронка, а не компании. На самом лендинге форма заявки заменена переходом на регистрацию компании: согласие человек отмечает там. Этот POST по-прежнему требует согласия, если заявку присылают напрямую.
У заявки status: new → tenant_created (в tenantId — заведённая компания) или rejected. Обработать можно только новую заявку; второй раз — 409 «Заявка уже обработана».
POST /api/leads/:id/create-tenant — компания из заявки, это и есть одобрение. Тело: { name, inn?, ownerEmail? } — ИНН и почта владельца по умолчанию из заявки, оператор может поправить опечатку. Компания заводится активной и без членов: владельцу уходит приглашение с ролью admin (как в /api/invites — пароль он задаст сам по одноразовой ссылке). Ответ 201: { lead, tenant: { id, name }, invite: { invite, mailSent, link } }; link не пустой, только если письмо не ушло — тогда её передают сами.
Отказы: ИНН с неверной контрольной суммой — 400 с fields.inn; компания с таким ИНН уже есть — 409 «Компания с этим ИНН уже подключена». Сбой посреди заведения откатывает всё и возвращает заявке статус new — повтор проходит.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/leads |
JWT, оператор платформы |
POST |
/api/leads |
без авторизации |
POST |
/api/leads/:id/create-tenant |
JWT, оператор платформы |
POST |
/api/leads/:id/reject |
JWT, оператор платформы |
Журналы писем — /api/letters
Входящие и исходящие письма компании — как в делопроизводстве: регистрационный
номер, дата, корреспондент, тема, исполнитель и срок. Нумерация у входящих и
исходящих своя, сквозная и с января заново: Вх-2026/0001, Исх-2026/0001.
Номер выдаёт серия журнала — атомарно, и если письмо не сохранилось, номер
возвращается в серию. Серии журналов в /api/invoice-series не видны.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/letters |
JWT; раздел «Письма» — просмотр |
GET |
/api/letters/correspondents |
JWT; раздел «Письма» — просмотр |
GET |
/api/letters/executors |
JWT; раздел «Письма» — просмотр |
GET |
/api/letters/:id |
JWT; раздел «Письма» — просмотр |
POST |
/api/letters |
JWT; раздел «Письма» — изменение |
PATCH |
/api/letters/:id |
JWT; раздел «Письма» — изменение |
POST |
/api/letters/:id/register |
JWT; раздел «Письма» — изменение |
POST |
/api/letters/:id/status |
JWT; раздел «Письма» — изменение |
POST /api/letters принимает direction (incoming, outgoing), date
(ГГГГ-ММ-ДД — дата получения входящего или регистрации исходящего),
correspondent ({ counterpartyId, name } — контрагент из справочника или
просто название; название хранится снимком), subject, channel (post,
registered_post, courier, email, edo, in_person) и необязательные
summary, executorId (участник компании), dueOn, signerName,
trackNumber, senderNumber, senderDate, resolution, inReplyToId,
subjects — о чём письмо: [{ type, id }], type — contract, invoice
или project (заменяет список целиком; в ответе — с подписью label;
объекта нет в вашей компании — 400). Фильтр журнала subject=<тип>:<id> —
письма о договоре, счёте или проекте.
Входящее регистрируется сразу — получает номер и статус new; дата должна
быть в текущем году: задним годом письмо не регистрируется (409).
Исходящее создаётся черновиком draft без номера, номер выдаёт
POST /api/letters/:id/register (статус registered).
PATCH принимает те же поля без direction. Аннулированное письмо не
правится (409); дату зарегистрированного письма нельзя перенести в другой
год — номер выдан за год (409).
POST /api/letters/:id/status — { status, date?, note? } по таблице
переходов LETTER_TRANSITIONS. Исходящее: registered → sent → delivered
→ answered → closed; входящее: new → in_progress → answered →
closed; аннулировать (cancelled) можно всё, кроме закрытого, — с
обязательной причиной в note (иначе 400). sent и delivered ставят
sentOn/deliveredOn — date или сегодня. Недопустимый переход и
registered через этот маршрут — 409. Аннулирование номер не возвращает.
Фильтры списка: direction (обязателен), status, executorId,
counterpartyId, from, to, q (номер, корреспондент, тема, исходящий
номер отправителя), overdue=1 — срок прошёл, а письмо не в answered,
closed, cancelled; senderNumber — точный исходящий номер отправителя
без учёта регистра и пробелов по краям, для проверки дубля входящего. Новые
сверху, не больше 500.
GET /api/letters/correspondents?q= — до 20 подсказок: контрагенты по
названию или началу ИНН и названия, уже встречавшиеся в письмах без
контрагента.
GET /api/letters/executors — кого можно поставить исполнителем: участники
компании, [{ id, name }] без почты и ролей. Доступен всем ролям, в отличие
от /api/users; executorId при сохранении проверяется по этому же списку.
Ошибки проверки — 400 { error, fields }, где fields — сообщение у поля.
Первые шаги компании — /api/onboarding
Чек-лист на главной кабинета: что осталось новой компании до первого счёта. Шаг пройден по данным, а не галочкой: requisites — в настройках есть название и ИНН (без них выставление отвечает 409), bankAccount — подключён расчётный счёт, project — заведён проект (необязательный шаг), invoice — есть счёт не в черновике. complete — пройдены все обязательные.
egressIps — исходящие IP сервера «Потока» (из EGRESS_IPS). Токен Т-Банка привязан к IP: до ввода токена эти адреса добавляют в разрешённые для него в Т-Бизнесе. Пустой список — адрес не настроен.
{
"steps": [
{ "key": "requisites", "done": true, "optional": false },
{ "key": "bankAccount", "done": false, "optional": false },
{ "key": "project", "done": false, "optional": true },
{ "key": "invoice", "done": false, "optional": false }
],
"complete": false,
"egressIps": ["203.0.113.10"]
}
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/onboarding |
JWT; раздел «Главная» — просмотр |
Виды платежей — /api/payment-kinds
canSettleInvoice определяет, может ли доход такого вида быть оплатой счёта: от него зависит, уйдёт ли поступление из очереди неразнесённых.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/payment-kinds |
JWT; раздел «Справочники» — просмотр |
POST |
/api/payment-kinds |
JWT; раздел «Справочники» — изменение |
DELETE |
/api/payment-kinds/:id |
JWT; раздел «Справочники» — управление |
POST |
/api/payment-kinds/:id/archive |
JWT; раздел «Справочники» — изменение |
PATCH |
/api/payment-kinds/:id |
JWT; раздел «Справочники» — изменение |
Зарплатные ведомости — /api/payroll-registers
Все маршруты требуют JWT, прав money.pay и hr.pii: GET — просмотра раздела
«Сотрудники», POST — изменения раздела.
| Метод | Маршрут | Назначение |
|---|---|---|
POST |
/api/payroll-registers/preview |
Проверить ведомость: `{period: "ГГГГ-ММ", kind: "advance" |
POST |
/api/payroll-registers |
Создать проверенную ведомость: те же поля и sourceHash из превью |
GET |
/api/payroll-registers/:id/export |
Экспорт поручений сотрудникам, формат 1С 1.03, windows-1251 |
POST |
/api/payroll-registers/:id/confirm |
Подтвердить выплату сотруднику: {employeeId, transactionId} |
Реквизиты сотрудника задаются в payout карточки, шифруются в базе и доступны только с hr.pii.
Ответы ведомости показывают маску счёта. Окончательная ведомость требует закрытого месяца.
В обоих POST необязательный bankAccountId выбирает активный счёт компании; превью показывает
банк и маску счёта плательщика. Чужой или архивный счёт — 404.
Повторное создание по тому же источнику возвращает существующую ведомость; изменённый расчёт
и устаревший экспорт блокируются. После нового превью rebuild: true в POST создаёт новую
неизменяемую ревизию и помечает прежнюю stale. Подтверждённую ведомость пересоздавать нельзя.
Экспорт не считается выплатой. Подтверждение проверяет сумму,
счёт, получателя, вид и месяц банковской операции и сохраняет индивидуальные суммы дохода и НДФЛ.
Налоговые платёжные поручения — /api/payment-orders
Все маршруты требуют JWT и права money.pay: GET — просмотра раздела
«Налоги к уплате», POST и PUT — изменения раздела.
| Метод | Маршрут | Назначение |
|---|---|---|
GET |
/api/payment-orders/sfr-recipients |
Отделения с проверенным справочником на текущий день компании; сохранённый выбор и ручные реквизиты |
PUT |
/api/payment-orders/sfr-recipient |
Сохранить `{branchId: string |
POST |
/api/payment-orders/preview |
Подбор непогашенных обязательств: `{period, channel: "ens" |
POST |
/api/payment-orders |
Создать проверенное поручение: те же поля и sourceHash |
GET |
/api/payment-orders/:id/export |
Экспорт поручения в формате 1С 1.03, windows-1251 |
Период — месяц ГГГГ-ММ, квартал ГГГГ-Qn или год. ЕНП учитывает оплату, ранний зачёт и
остаток ЕНС; травматизм исключён из ЕНП и готовится отдельным каналом injury.
Для СФР нужны проверенные реквизиты регионального отделения (payee): получатель,
ИНН, КПП, счёт, банк, БИК, корреспондентский счёт и ОКТМО. Для текущих взносов на травматизм
формируются КБК 79710212000061000160, статус плательщика 08 и КПП плательщика из настроек
(у ИП — 0); поля 106–109 — 0.
Изменение обязательств или реквизитов после проверки блокирует создание/экспорт.
После повторного превью rebuild: true в POST создаёт новую ревизию по оставшейся сумме,
старый экспорт блокируется; расчёт учитывает только активные версии документов.
Выгрузка не означает оплату; факт оплаты — банковская операция и привязка к обязательству.
Ответ справочника содержит on, selectedBranchId, manualPreference и recipients с
branchId, branchName, status (ready, no_data, stale, ambiguous) и recipient.
Массив включает и отделения без проверенной записи (no_data), sourceUrl ведёт на официальный ресурс отделения.
Ручной manualPreference содержит payee, sourceUrl и checkedOn; ссылка может быть только HTTP(S),
дата сверки не может быть позже текущего дня компании.
Для injury выбранный sfrBranchId разрешается сервером по официальному версионированному
справочнику на дату документа; переданный вместе с ним payee не используется.
Недоступная, устаревшая или неоднозначная версия блокирует подготовку. КБК подтверждён только для 2026 года: в другие годы подготовка СФР блокируется и при ручном вводе. Ручной payee
остаётся доступен при отсутствии sfrBranchId и отмечается recipientProvenance.status = manual;
для проверенной записи статус verified и сохраняются её источник и даты. Выбор и ручные
реквизиты изолированы по компании и меняются только с правами изменения налогов и money.pay.
При экспорте проверяются сохранённая дата поручения и актуальная на момент экспорта версия справочника; устаревшая или изменившаяся версия блокирует экспорт.
Необязательный bankAccountId в обоих POST выбирает активный счёт компании.
Номера поручений сохраняются при создании и повторном экспорте; общий атомарный счётчик
по компании, счёту и дате предотвращает пересечение зарплатных, ЕНП и СФР-поручений.
Зарплата — /api/payroll
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/payroll/summary |
JWT; раздел «Сотрудники» — просмотр |
Без права «Зарплаты и ПДн сотрудников» — только итоги месяца (total*,
тариф, пояснения): employees: [] и employeesHidden: true. Итоги те же,
что у бухгалтера (решение владельца: ФОТ без права — общей суммой).
Штатное расписание — /api/positions
Должности: название, оклад за полную ставку (salaryKopecks), число
штатных ставок (staffRateMilli, в тысячных: 2500 — две с половиной) и
признак руководителя организации (isHead). В ответе — сколько ставок уже
занято работающими (occupiedRateMilli) и сколько на должности человек.
Без права «Зарплаты и ПДн сотрудников» поля salaryKopecks в ответе нет
(оклад должности — оклад каждого на ней), а PATCH с salaryKopecks —
403 с кодом access_denied.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/positions |
JWT; раздел «Сотрудники» — просмотр |
POST |
/api/positions |
JWT; раздел «Сотрудники» — изменение, право «Зарплаты и ПДн сотрудников» |
PATCH |
/api/positions/:id |
JWT; раздел «Сотрудники» — изменение |
DELETE |
/api/positions/:id |
JWT; раздел «Сотрудники» — управление |
POST |
/api/positions/:id/archive |
JWT; раздел «Сотрудники» — изменение |
Сотрудник занимает должность (positionId) на ставку rateMilli (1000 —
полная, 500 — половина). При salaryFromPosition: true оклад считается
сам: оклад должности × ставка, до копейки, и пересчитывается при правке
оклада должности. Иначе оклад задаётся вручную. employmentBasis:
labor_contract — трудовой договор, no_contract — руководитель —
единственный участник без трудового договора; такое основание допустимо
только на должности руководителя, при переводе на другую должность оно
сбрасывается. Должность с сотрудниками не удаляется (409, archivable).
Реквизиты по ИНН — /api/requisites
Реквизиты организации или ИП из ЕГРЮЛ/ЕГРИП через DaData — для кнопки «Заполнить по ИНН». Ничего не сохраняет.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/requisites/by-inn |
JWT; раздел «Контрагенты» — изменение |
Параметр inn — 10 или 12 цифр. Ответ: kind (legal, entrepreneur), name, fullName, kpp, ogrn,
address, oktmo, ifnsCode (у организации — первые четыре цифры КПП,
иначе инспекция по адресу), directorName, okved, status («действует»,
«ликвидируется»…). Неверный ИНН — 400, не найден — 404, сервис не
ответил — 502, ключ DADATA_API_KEY не задан или не принят — 503.
Единый налоговый счёт — /api/ens
Сальдо ЕНС и погашение налогов к уплате. Платёж с видом «Единый налоговый
платёж» (код ens, распознаётся по «ЕНП», «единый налоговый платёж» или КБК
18201061201010000510) пополняет счёт; в срок уплаты обязательство из
закрытого периода списывается с остатка, непокрытое — долг. Деньги дня
раскладываются по п. 8 ст. 45 НК РФ: недоимка по НДФЛ, НДФЛ со сроком в этот
день, прочая недоимка от ранней к поздней, прочие налоги срока; на налоги
одного срока и одной ступени денег мало — пропорционально (п. 10 ст. 45);
пени и штрафы не считаются. По уведомлению (считается поданным в последний
день срока подачи, пока не отмечена фактическая подача) переплата зачитывается заранее в счёт НДФЛ и взносов на
следующий рабочий день (п. 7, 9 ст. 78 НК РФ, абз. 1; уведомление на
несколько месяцев по абз. 2 не моделируется) и возвращается, если возник
долг. Годы, закрытые до этой очереди, и сданные до неё кварталы считаются
прежней (docs/research/ens-order-corpus.md). Ручная
привязка применяется раньше: так учитываются платежи не через ЕНП, например
взносы на травматизм.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/ens |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/ens/allocations |
JWT; раздел «Налоги к уплате» — изменение, право «Готовить платежи» |
DELETE |
/api/ens/allocations/:id |
JWT; раздел «Налоги к уплате» — изменение, право «Готовить платежи» |
GET |
/api/ens/notice |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/ens/notice/file |
JWT; раздел «Налоги к уплате» — просмотр, право «Готовить платежи» |
GET |
/api/ens/payment |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/ens/payment/file |
JWT; раздел «Налоги к уплате» — просмотр, право «Готовить платежи» |
PUT |
/api/ens/obligations/:id/notice-filed |
JWT; раздел «Налоги к уплате» — изменение |
DELETE |
/api/ens/obligations/:id/notice-filed |
JWT; раздел «Налоги к уплате» — изменение |
GET |
/api/ens/balances |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/ens/balances |
JWT; раздел «Налоги к уплате» — изменение, право «Готовить платежи» |
GET — balanceKopecks (больше нуля — переплата, меньше — долг; без
зачтённого заранее, как в личном кабинете), overdueKopecks,
upcomingKopecks, reservedKopecks (зачтено заранее по уведомлениям и ждёт
срока), obligations (обязательство, paidKopecks, reservedKopecks,
reservedOn — день ожидаемого зачёта по уведомлению,
status: paid, partial, overdue, covered — покрыто
переплатой, upcoming; allocations — каким платежом и когда погашено;
manual — ручные привязки) и payments (сколько каждого платежа ушло на
налоги). POST /allocations — { transactionId, obligationId, amountKopecks }; сумма не больше остатка платежа и налога за вычетом
других ручных привязок (400), повтор той же пары заменяет сумму.
В obligation — отметка noticeFiledOn (ГГГГ-ММ-ДД) и noticeFiledByName.
Отметка «уведомление подано». PUT /obligations/:id/notice-filed —
{ on? } (дата подачи, по умолчанию сегодня в поясе компании; будущая или
несуществующая — 400 с fields.on; налог без уведомления — 400 без
полей); DELETE — снять. Отвечают тем же, что
GET /api/ens. Подано позже срока подачи — ранний зачёт переносится на
следующий рабочий день после отмеченной даты (п. 7 ст. 78 НК), раньше срока —
считается по сроку, как без отметки. Налог без уведомления (НДС, УСН за
IV квартал, взносы ИП 1%) — 400; срок уплаты в закрытом году или сданном
налоговом квартале — 409: расчёт за них закреплён. Только люди (JWT).
Период открыли заново — обязательство снимается вместе с отметкой.
Период, по которому что-то уже погашено, заново не открыть (409 на
/api/periods/reopen). Взносы, погашенные с ЕНС, в оценке налога
уменьшают УСН «Доходы» и входят в расходы на «Доходах минус расходы».
Сверка с налоговой: POST /balances — { date (ГГГГ-ММ-ДД), balanceKopecks (плюс — переплата, минус — долг, как в личном кабинете), note? }, сальдо ЕНС
по данным налоговой на дату; на ту же дату — правка, будущая дата — 400.
Ответ и GET /balances (последние 24, свежие сверху) — с расчётом «Потока» на
ту же дату: computedKopecks и differenceKopecks (налоговая минус расчёт).
Последняя сверка — пункт ens-balance чек-листа закрытия квартала: не
блокирует; расхождение — «Проверьте», сальдо не вносили — «не проверялось».
Уведомление об исчисленных суммах (КНД 1110355, формат 5.03) — за месяц
уплаты ?month=ГГГГ-ММ: строки по НДФЛ, взносам и авансам УСН со сроком в
этом месяце (у НДС и налога на прибыль декларация раньше срока). НДФЛ
разложен по КБК ставок шкалы по расчёту сотрудников из закрытого месяца;
период НДФЛ — месяц удержания (месяц уплаты, номер 01–03), взносов — месяц
начисления, авансов УСН — 34/01–03. КБК — из налоговых норм (kbk.codes).
GET /notice — состав, срок подачи (deadline, 25 число с переносом) и
warnings: без ИНН, КПП, ОКТМО, кода инспекции или руководителя файл не
выдаётся (409). GET /notice/file — XML в windows-1251 с именем
UT_UVISCHSUMNAL_…; месяц без сроков — 404.
Платёж на ЕНП. GET /payment (необязательный bankAccountId) — сколько
заплатить сейчас: налоги ближайшего срока плюс долг минус то, что уже лежит
на счёте (amountKopecks, nearestDueDate, overdueKopecks,
spareKopecks), получатель и КБК из норм ens.payee и kbk.codes, строка
QR-кода по ГОСТ Р 56042-2014 (qrPayload) и warnings. GET /payment/file
(amount в копейках, number, bankAccountId) — платёжное поручение в
формате 1С «Клиент банка» 1.03 (1c_to_kl.txt, windows-1251) с реквизитами
ЕНП по приказу Минфина № 58н: статус 01, КПП плательщика 0, ОКТМО и
основание 0, назначение «ЕНП». Без ИНН или банковского счёта — 409.
Закрытие периодов — /api/periods
Пока период открыт, налоги считаются на лету — по нормам и условиям на
его даты. Закрытие сохраняет расчёт целиком вместе с нормами
(rulesUsed), по которым он сделан: новая версия нормы, правка оклада или
режима закрытый период не меняют. GET /api/payroll/summary и
GET /api/reports/tax-estimate для закрытого периода отдают сохранённый
расчёт с отметкой closed (кто и когда закрыл), для открытого — closed: null.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/periods |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/periods/checklist |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/periods/late |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/periods/adjustments |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/periods/adjustments/preview |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/periods/adjustments |
JWT; раздел «Налоги к уплате» — изменение, право «Закрывать период» |
POST |
/api/periods/adjustments/:id/complete |
JWT; раздел «Налоги к уплате» — изменение, право «Закрывать период» |
POST |
/api/periods/correction |
JWT; раздел «Налоги к уплате» — изменение, право «Закрывать период» |
GET |
/api/periods/filed |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/periods/filed |
JWT; раздел «Налоги к уплате» — изменение, право «Сдавать отчётность» |
POST |
/api/periods/unfile |
JWT; раздел «Налоги к уплате» — изменение, право «Сдавать отчётность» |
POST |
/api/periods/close |
JWT; раздел «Налоги к уплате» — изменение, право «Закрывать период» |
POST |
/api/periods/reopen |
JWT; раздел «Налоги к уплате» — изменение, право «Закрывать период» |
Тело — { scope, period }: scope — payroll (месяц зарплаты, period
— ГГГГ-ММ) или tax (квартал налога, ГГГГ-Qn). Закрытие даёт
обязательства к уплате (obligations: taxKind, dueDate,
amountKopecks): НДФЛ и взносы за месяц — до 28 числа следующего месяца;
УСН за I–III квартал — до 28 числа месяца после квартала, за год — до 28
марта (ИП — 28 апреля); НДС — тремя равными долями до 28 числа трёх
месяцев после квартала; налог на прибыль — как УСН у организации. Срок,
выпавший на выходной или нерабочий праздничный день, переносится на следующий
рабочий день по производственному календарю (ст. 112 ТК РФ и постановления о
переносе выходных, п. 7 ст. 6.1 НК РФ).
Второе закрытие — 409, период, который ещё не начался, — 400.
reopen с reason (не короче 5 символов) открывает период заново:
обязательства снимаются, запись о закрытии остаётся с отметкой, кто и
почему открыл. GET /api/periods?year= — действующие закрытия года.
Чек-лист квартала. GET /api/periods/checklist?period=2026-Q3 —
пункты проверки перед закрытием. У каждого: key, title, status
(ok | warn | fail), blocking (мешает ли закрытию, когда fail),
details (что не так, по-русски) и link ({label, path} — экран для
исправления, либо null). Проверяются: квартал закончился; выписки по всем
счетам загружены после конца квартала; все платежи разнесены по видам;
поступления привязаны к счетам и «прочие» поступления; оплаченные счета
закрыты документами; нет черновиков документов; у расходов есть документы
(«Доходы минус расходы»); месяцы зарплаты закрыты; заполнены ОКТМО и ИФНС;
порог НДС на УСН; предыдущий квартал года закрыт; нет просроченных
налогов на ЕНС. Пункт «остаток сходится с выпиской» сверяет сальдо 51 по журналу с остатком
банка на конец квартала (checkBankBalances): расхождение или отсутствие
остатка — warn, закрытие не блокирует. Для плательщика НДС незакрытые счета и
черновики документов блокируют, на УСН без НДС — предупреждение. Ответ:
items, canClose, confirmRequired (ОСНО), closed, late.
POST /api/periods/close для налога отвечает 409 с
code: "checklist_failed" и списком blockers, если есть блокирующий
fail. На ОСНО тело закрытия несёт confirm: true — подтверждение, что
суммы сверены (оценка ОСНО — грубый ориентир); без него 409 с
code: "confirm_required". Чек-лист на момент закрытия сохраняется в записи
о закрытии.
Запрет правок в закрытом периоде. Правка, меняющая расчёт закрытого
периода, отвечает 409 с code: "period_closed", scope, period и
текстом со ссылкой «откройте период заново»: платёж — смена вида,
taxable, deductible, grossAmountKopecks (присланное без изменений не
считается; не блокируются комментарии, категория, повторяемость, договор,
а также платёж, пришедший после закрытия); POST /api/sales-documents/:id/issue
и /cancel с датой закрытого квартала — только у плательщика НДС (ОСНО,
УСН с НДС), на УСН без НДС выставление отвечает warning;
POST /api/tax-profile с effectiveFrom не позже конца закрытого
периода налога или зарплаты; сотрудники — премия и даты приёма/увольнения
в закрытом месяце, оклад и ставка при закрытом текущем месяце.
POST /api/transactions/recategorize закрытые кварталы пропускает.
Операции после закрытия. Импорт выписки не блокируется: операция с
датой в закрытом квартале сохраняется с afterCloseOf (ключ квартала, поле
есть в TransactionDto). GET /api/periods/late?year= — по закрытым
кварталам года число таких операций и суммы поступлений и списаний
(count, incomeKopecks, expenseKopecks) — баннер на странице «Налоги».
Закрытие квартала заново снимает пометки.
Годовое закрытие УСН. Закрытие IV квартала на УСН — расчёт за год, а не
аванс. Налог за год (минимальный налог 1% на «Доходах минус расходы» —
только здесь, п. 6 ст. 346.18 НК РФ) минус авансы I–III кварталов с учётом
внесённых корректировок (в периоде ошибки) даёт usn-обязательство на
доплату: срок 28 марта (ООО) или 28 апреля (ИП) с переносом; если авансы
больше налога, обязательства нет, разница — «к уменьшению». Для ИП
добавляется обязательство contributions с periodKey: "ГГГГ" — 1% с
дохода свыше 300 000 ₽ до 1 июля следующего года (уведомление по нему не
подаётся). Итоги записываются в закрытие как annual (year,
yearTaxKopecks, regularTaxKopecks, minimumTaxKopecks,
minimumTaxApplied, advancesKopecks, advanceAdjustmentsKopecks,
dueKopecks — плюс доплата, минус уменьшение, ipContribution, links на
/api/reports/kudir и /api/reports/usn-declaration) и видны в
GET /api/periods и в state.annual чек-листа; до закрытия — в
annualPreview. Снимок IV квартала несёт ту же доплату
(usnTaxKopecks, ytd.priorAdvancesKopecks, ytd.dueKopecks).
Взносы ИП за себя. Фиксированные взносы (ip.fixedContributions, ст. 430
НК РФ) выпускаются обязательством contributions с part: "ip_fixed",
periodKey: "ГГГГ" и сроком 28 декабря (п. 2 ст. 432) при первом закрытии
квартала года — не в конце года, чтобы «Налоги к уплате» и календарь
показывали взнос заранее, а платить его можно было частями. Один раз за год;
у ООО их нет. Сумма — за полный год (при регистрации ИП в течение года она
пропорциональна). 1% с дохода сверх 300 000 ₽ — part: "ip_additional", при
годовом закрытии. Уменьшение налога на эти взносы считает оценка налога, не
обязательство.
Корректировки вместо переоткрытия. reopen работает, только пока по
периоду нет оплаты, отметки «сдано» и корректировок; иначе 409 с
code: "reopen_blocked" и correctionLink (для квартала налога).
Исправление закрытого квартала вносится корректировкой (п. 1 ст. 54 НК РФ:
ошибку исправляют в периоде ошибки, в текущем — только если она привела к
переплате). Поток: (1) операция пришла задним числом (afterCloseOf) —
её можно классифицировать сразу; правка уже учтённой операции сначала
требует POST /api/periods/correction {period, reason} — «начать
корректировку»: платежи и условия квартала можно править, закрытый снимок
не меняется; (2) GET /api/periods/adjustments/preview?period=2026-Q1 —
закрытие против данных сейчас: lines (taxKind, baselineKopecks,
currentKopecks, deltaKopecks), direction (underpaid | overpaid |
none), allowedRecognizedIn, nextStatus, warnings (пени, нужный
отчёт); (3) POST /api/periods/adjustments {period, reason, recognizedIn?} — внести; разница пересчитывается на сервере. Недоплата
становится обязательством с прежним сроком уплаты (adjustmentId в
obligations), переплата в периоде ошибки уменьшает обязательства, в
текущем периоде (recognizedIn: "current_period", только переплата) —
обязательства не трогаются. Статус: needs_amended_notice (авансы УСН),
needs_amended_declaration (год УСН, НДС, прибыль) или done;
POST /api/periods/adjustments/:id/complete — уточнённый отчёт подан.
Внесение снимает пометки afterCloseOf и завершает идущую корректировку.
Уведомление за месяц срока (/api/ens/notice) несёт исправленную сумму
одной строкой; порядок подачи уточнённого уведомления не подтверждён.
409 от запрета правок (period_closed) несёт correctionLink.
Сдано. POST /api/periods/filed {scope, period, on, note?} —
отметить, что отчётность за закрытый период подана (on — ГГГГ-ММ-ДД, не в
будущем и не раньше начала периода); POST /api/periods/unfile снимает.
GET /api/periods/filed — все отметки (для календаря сроков: срок отчёта за
сданный период показывается как «сдано»). Отметка есть в GET /api/periods
(filed), в state чек-листа квартала. Квитанция или протокол — обычный
документ библиотеки со связью {type: "period", id: <id закрытия>}.
Налоговый профиль — /api/tax-profile
Налоговые условия компании по периодам: режим (taxRegime), региональная
ставка УСН (usnRatePercent, null — общая из налоговых норм), IT-льгота
(isItCompany), реестр российского ПО (hasRegistrySoftware), пониженный
тариф взносов МСП (mspReducedTariff), дни выплаты зарплаты
(paySchedule) — каждое с даты effectiveFrom. Оценка налога берёт условия
на начало квартала, расчёт зарплаты — на месяц: смена режима с января не
пересчитывает прошлый год.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/tax-profile |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/tax-profile |
JWT; раздел «Налоги к уплате» — изменение |
DELETE |
/api/tax-profile/:id |
JWT; раздел «Налоги к уплате» — управление |
GET |
/api/tax-profile/pay-schedule |
JWT; раздел «Налоги к уплате» — просмотр |
PUT |
/api/tax-profile/pay-schedule |
JWT; раздел «Налоги к уплате» — изменение |
GET — current (условия на сегодня) и periods. Пока истории нет,
период один — исходный, из настроек (id: null, с 2000-01-01). POST
заводит период с даты или правит период на ту же дату; первая запись
сначала сохраняет исходные условия, чтобы прошлое не изменилось.
Региональная ставка — больше нуля и не выше общей на ту дату (400).
Удалить можно только период, который ещё не начался (409).
PATCH /api/settings с режимом или льготами при наличии истории заводит
период с сегодняшнего дня; без истории — уточняет исходные условия.
Дни выплаты зарплаты (paySchedule, ТК РФ ст. 136). Для каждой
выплаты: title, day (1–31, в коротком месяце — последний день),
nextMonth (окончательный расчёт платится в следующем за периодом месяце)
и период месяца начисления periodFrom–periodTo (1–31, 31 = конец
месяца). Устаревший флаг withholdNdflFromAdvance сохранён для чтения
старых данных: НДФЛ удерживается при каждой выплате независимо от него. GET /pay-schedule отдаёт {effectiveFrom, schedule, pending, warnings}:
pending — уже записанный график с будущей датой ({effectiveFrom, schedule}), текущий месяц догуливает по прежним дням. PUT /pay-schedule
записывает график с новой даты — создаёт период условий (по умолчанию с
начала следующего месяца; первая настройка действует сразу; телом можно
передать effectiveFrom — ГГГГ-ММ-ДД) и отвечает {effectiveFrom, schedule, warnings, closedMonths}. Прошлые месяцы не переписываются;
будущие периоды получают тот же график, пока по ним ничего не закрыто;
closedMonths — закрытые месяцы зарплаты с даты смены, которые останутся
со своим снимком расчёта. warnings — не запреты: периоды не
покрывают месяц, выплата позже 15 календарных дней после периода, отсчёт —
от календарного конца периода (ч. 6 ст. 136 ТК), между выплатами больше
полумесяца. По графику НДФЛ месяца раскладывается по датам выплаты:
удержание 1–22 числа платится 28-го этого месяца, 23 –
конец — 5-го следующего, 23–31 декабря — в последний рабочий день года
(п. 6 ст. 226 НК). Дата дохода — день каждой выплаты, включая аванс
(подп. 1 п. 1 ст. 223 НК, действует с 2023 года). Отключить удержание
старым флагом withholdNdflFromAdvance нельзя. У обязательства появляются band и
withheldIn (месяц удержания — код периода в уведомлении; срок подачи —
25-е месяца удержания для полосы 1–22 и 3-е следующего месяца для полосы
23–конец, п. 9 ст. 58 НК). Без графика — как раньше: одной суммой к 28-му
следующего месяца; такая оценка не подтверждает удержанный налог для сдачи.
PayrollSummary.payouts хранит отдельные выплаты каждого сотрудника:
доход, НДФЛ и сумму на руки с разбивкой по ставкам. Налог рассчитывается
последовательно по выплатам, не пропорциональным делением налога компании.
Банковская метка payrollMonth связывает операцию с месяцем начисления,
а подтверждение строки ведомости создаёт PayrollPayoutRecord — проверенный
источник для уведомлений и 6-НДФЛ. Дата операции берётся
календарным днём в часовом поясе компании. Календарь сроков
(/api/reports/tax-deadlines) собирает сроки НДФЛ по графику каждого
месяца (profile.payScheduleByMonth, 12 значений с января): смена дней
выплаты не перерисовывает прошедшие месяцы. Кроме того, профиль несёт
payoutFactsByMonth и headOnStaffByMonth (12 значений | null): были ли
выплаты физлицам и числился ли руководитель по месяцам. По ним сроки
получают relevance: suppressed с suppressReason — когда по фактам
периода срок не возникает (нет выплат — нет НДФЛ; нет дохода — нет аванса
УСН; нет НДС — нет сроков НДС); взносы (contributions-payment, rsv)
при этом остаются реальными, если руководитель числится, — взносы с
федерального МРОТ платят и без выплат (с 2026). Взносы на травматизм
(efs1-injury, efs1-stazh, injury-payment) — наоборот: минимальная
база (МРОТ) на 125-ФЗ не распространяется, без выплат физлицам их нет.
У каждого срока есть periodKey (ГГГГ-Qn или ГГГГ-ММ) — тот же формат,
что у требований к отчётности. Квартальные сроки подавляются, только когда
закрыты ВСЕ три месяца квартала: закрытый пустой месяц не должен прятать
реальные сроки за ещё не закрытыми; помесячные сроки (НДФЛ, взносы,
персонифицированные) оцениваются по своему месяцу. Месяц без фактов
(null — период не закрыт) не подавляется: без данных поведение прежнее.
Подавленные сроки остаются в items (их видно по чекбоксу «показать
неприменимые»), а коротким списком «почему срок не показан» они собраны в
excluded: {ruleId, periodKey, suppressReason}. Напоминания письмом (за 5 дней
до срока и в день срока) по подавленным срокам не приходят: письмо о сроке,
которого на самом деле нет, — шум. Уведомления на экране
«Налоги» собираются по месяцу подачи (у НДФЛ с полосой — от месяца
удержания), и у каждой строки файла свой срок подачи noticeDue.
Факты деятельности и требования к отчётности — /api/reports/activity-facts, /api/reports/filing-requirements
Слой фактов (план автоматизации §3): «что реально было» вместо шаблона «у компании есть сотрудники».
GET /api/reports/activity-facts?year — факты по месяцам года:
months (ключ ГГГГ-ММ) — начисления физлицам, фактические выплаты из
выписки (hasPayouts, payoutCount), удержанный НДФЛ, руководитель на
штате (headOnStaff) и взносы с его минимальной базы
(headContributionsDueKopecks), доход УСН и НДС к уплате (по закрытым
кварталам); aggregate — суммы года. Месяц без закрытия в months отсутствует.
Удержанный НДФЛ — только из снимка закрытия месяца: обязательства (плановые
суммы к уплате) фактом удержания не считаются.
GET /api/reports/filing-requirements?year&quarter&month — по каждой форме
(artifact: ndfl_notice, ndfl6, rsv, personified, efs1,
usn_notice, usn_payment, usn_declaration, vat_declaration,
vat_payment, profit_declaration, profit_payment,
contributions_payment, injury_payment; periodKey — ГГГГ-Qn или
ГГГГ-ММ): required — существует ли обязанность по фактам периода,
pending — осталось ли действие (не сдано), reason — почему, blockers —
чего не хватает, sourceIds — источники. Панели «Отчётности» рендерятся по
этому списку: неприменимые формы не показываются. ?quarter= и ?month=
— ответ по одному периоду (квартал — его формы и месячные формы его
месяцев; месяц — только формы этого месяца).
Формы режима: у компании на ОСНО форм УСН в ответе нет, и наоборот.
Квартал оценивается, только когда закрыты все три его месяца; при неполных
данных обязанность НЕ отрицается: форма остаётся required и pending с
reason «Данных за период недостаточно: месяцы не закрыты». ЕФС-1 (травматизм,
125-ФЗ) — не РСВ: без выплат физлицам её не требуется (минимальная база
МРОТ на 125-ФЗ не распространяется).
Годовые формы (usn_declaration, profit_declaration) — период в ответе
String(year), а факт берётся за ВЕСЬ год, а не за IV квартал: сезонный
бизнес с доходом в I–III кварталах и пустым IV кварталом декларацию всё равно
подаёт. Год — с даты регистрации компании; если месяцы года неизвестны,
обязанность не отрицается (reason «данных недостаточно»).
«Сдано» — отправка в статусе accepted, delivered, sent, created или
prepared; needs_correction («нужна корректировка») и unknown сданными не
считаются: по ним форма остаётся pending. Факт оплаты для payment-форм
(usn_payment, vat_payment, contributions_payment, injury_payment)
учитывается с волны W5 (PaymentOrder) — пока оплата не закрывает pending.
Превью форм (/personified, /rsv, /ndfl6, /efs1) отдают
notRequiredReason: форма за период не сдаётся (нет выплат физлицам и
удержанного НДФЛ) — спокойное пустое состояние вместо блокеров; у
персонифицированных прежнее filingNotRequired (последний месяц квартала)
сохранено и входит в notRequiredReason.
Автоматизация — /api/automation
Журнал конвейера автоматизации (план автоматизации §1, волна W1): список
задач, которые конвейер собрался сделать и почему, с историей статусов и
письмами по стадиям. Волна W1 — каркас: из реальных триггеров планировщик
создаёт задачи-уведомления о ближайших налоговых сроках (≤ 3 дней), остальные
виды (payroll_register, payment_order, filing) появятся в волнах W4–W6.
GET /api/automation/tasks?status&kind&year — список задач. Фильтры:
status (scheduled, running, needs_review, draft,
awaiting_signature, accepted, done, failed, cancelled, stale),
kind (payroll_register, filing, payment_order, notification),
year — по ключу периода (2026-10, 2026-Q1). В каждой задаче:
naturalKey (ключ идемпотентности — одна активная задача на документ),
periodKey, dueDate, reason (почему задача появилась или пропущена),
sourceType/sourceIds (источник), status, attempts, lastError.
GET /api/automation/tasks/:id — карточка задачи: то же, что в списке, плюс
history[] (переходы статусов: at, from, to, reason, by) и
notifications[] (письма: стадия, получатель, статус, sentAt, attempts;
sent ставится только после успешной отправки — сбой SMTP письмо не теряет).
POST /api/automation/tasks/:id/retry — ручной повтор (заглушка W1):
снимает ошибку и ставит задачу обратно в очередь; выполняющийся под арендой —
409.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/automation/tasks |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/automation/tasks/:id |
JWT; раздел «Налоги к уплате» — просмотр |
POST |
/api/automation/tasks/:id/retry |
JWT; раздел «Налоги к уплате» — редактирование |
Фон конвейера выключен по умолчанию: env AUTOMATION_WORKER_ENABLED
оператора плюс вложенные флаги Settings.automation компании (секция
«Автоматизация» в настройках, все выключены по умолчанию). Выключение флага
прекращает НОВЫЕ задачи и письма, но не отзывает уже созданные черновики.
Налоговые нормы — /api/tax-rules
Ставки, пороги и суммы, по которым считаются налоги и взносы: НДФЛ, МРОТ, предельная база, тарифы взносов, ставки УСН, минимальный налог, порог НДС на УСН, налог на прибыль, НДС. У каждой нормы — версии с датой вступления в силу и источником (статья НК РФ, закон, постановление); расчёт берёт версию на дату операции, а не «ставку года».
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/tax-rules |
JWT; раздел «Справочники» — просмотр |
Ответ — по норме: code, label, unit (percent, kopecks, flag,
bands, tariff), действующая сегодня current и вся история versions.
Версии из кода — с id: null. Новую версию после выхода закона вносит
оператор платформы: POST /api/platform/tax-rules с code,
effectiveFrom (ГГГГ-ММ-ДД), value и source. Версию не правят;
удалить (DELETE /api/platform/tax-rules/:id) можно только ещё не
вступившую в силу — по действующей уже считали (409). Вторая версия на
ту же дату — 409.
Проекты — /api/projects
Код проекта — две цифры внутри номера счёта (ГГППНННН). Список сразу отдаёт деньги по каждому проекту.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/projects |
JWT; раздел «Проекты» — просмотр |
POST |
/api/projects |
JWT; раздел «Проекты» — изменение |
DELETE |
/api/projects/:id |
JWT; раздел «Проекты» — управление |
PATCH |
/api/projects/:id |
JWT; раздел «Проекты» — изменение |
Платформа — /api/platform
Раздел оператора платформы (isPlatformAdmin): компании, очередь на проверку и журнал фоновых задач (опрос выписки и сверка по компаниям — что сделано и какие ошибки). Выбор компании не нужен; внутрь компаний этот раздел не пускает — туда пускает только членство.
GET /tenants — все компании: статус, почта владельца (ownerEmail), число людей, когда попала в очередь (queuedAt) и последнее решение (reviewedAt, reviewedByName). ?status=pending — очередь на проверку: только компании, чей владелец подтвердил почту, дольше всех ждущие — первыми; другие значения status — просто фильтр.
POST /tenants/:id/approve — компания начинает работать, владельцу уходит письмо со ссылкой в кабинет; не подтвердившую почту одобрить нельзя (409). POST /tenants/:id/reject с { reason } — отказ, причина обязательна (её увидит владелец в письме и на экране статуса); ИНН отклонённой освобождается — в списке он виден, но зарегистрировать компанию с ним снова можно. Решение одно: по компании, которая уже не на проверке, — 409. Ответ обоих: { tenant, mailSent }. Каждое решение пишется в журнал — GET /tenants/:id/events: кто, когда, что и почему.
О каждой компании, попавшей в очередь, оператору уходит письмо на OPERATOR_EMAIL (если задан).
У каждой компании в списке — billing, сводка тарифа (как GET /api/billing). POST /tenants/:id/billing — ручная правка: { paidUntil?: "ГГГГ-ММ-ДД" | null, freeAccounts?, unlimited?, note }, причина обязательна и уходит в журнал. unlimited: true — все счета бесплатно (свои компании, партнёры); компания самого «Потока» (PLATFORM_TENANT_ID) без ограничений всегда. Продление, покрывающее выставленный неоплаченный период, закрывает долг.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/platform/jobs |
JWT, оператор платформы |
GET |
/api/platform/tax-rules |
JWT, оператор платформы |
POST |
/api/platform/tax-rules |
JWT, оператор платформы |
DELETE |
/api/platform/tax-rules/:id |
JWT, оператор платформы |
GET |
/api/platform/tenants |
JWT, оператор платформы |
POST |
/api/platform/tenants/:id/approve |
JWT, оператор платформы |
POST |
/api/platform/tenants/:id/billing |
JWT, оператор платформы |
GET |
/api/platform/tenants/:id/events |
JWT, оператор платформы |
POST |
/api/platform/tenants/:id/reject |
JWT, оператор платформы |
Акт сверки — /api/reconciliation
Сверка расчётов с контрагентом за период. Параметры — только в строке запроса,
все три обязательны: counterpartyId, from и to (ГГГГ-ММ-ДД,
включительно). Границы — календарные даты в часовом поясе компании
(settings.timezone): платёж 31-го в 23:30 по Москве остаётся в этом месяце.
Чужой или несуществующий контрагент — 404, неверные параметры — 400.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/reconciliation |
JWT; раздел «Платежи» — просмотр |
GET |
/api/reconciliation/pdf |
JWT; раздел «Платежи» — просмотр |
GET |
/api/reconciliation/print |
JWT; раздел «Платежи» — просмотр |
GET /api/reconciliation отдаёт расчёт: opening (сальдо на начало),
lines (строки периода: date, kind, document, debitKopecks,
creditKopecks), turnover (обороты), closing (сальдо на конец) и
conclusion — итоговая фраза (text: «задолженность отсутствует» или
«задолженность в пользу … сумма руб. (сумма прописью)») и inFavorOf
(company — контрагент должен нам, counterparty — мы ему, null —
расчёты сведены). Сальдо — пара debitKopecks/creditKopecks, заполнена
одна сторона. notes — чего в расчёте нет: например, полученные документы без
суммы. Контрагент без движений — нулевой акт.
Учёт ведётся по нашей стороне, как в 1С. Дебет — наши выставленные (не
аннулированные, не черновики) документы продажи и исходящие платежи
контрагенту с его ИНН. Кредит — входящие платежи: с его ИНН (в том числе ещё
не разнесённые) и привязанные к его счетам; а также полученные от него
закрывающие документы библиотеки (акт, накладная, УПД, чек) с указанной
суммой. Сальдо на начало — все движения до from.
pdf — бланк файлом (Content-Disposition: attachment, имя вида
akt-sverki-<from>_<to>.pdf), print — тот же бланк HTML-страницей для
печати из браузера. Форма — по образцу 1С:БП, альбомная: слева «По данным»
нашей компании, справа пустая половина для контрагента; факсимиле подписи и
печать ставятся по флагу настроек для первичных документов.
Отчёты — /api/reports
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/reports/cash-flow-forecast |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/counterparties |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/pnl |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/pnl/series |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/tax-calendar |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/tax-deadlines |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/activity-facts |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/filing-requirements |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/contract-periods |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/missing-docs |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/unclosed-invoices |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/tax-estimate |
JWT; раздел «Отчёты» — просмотр |
GET |
/api/reports/kudir |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/kudir/pdf |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/kudir/xlsx |
JWT; раздел «Налоги к уплате» — просмотр, право «Массовые выгрузки» |
GET |
/api/reports/usn-declaration |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/usn-declaration/pdf |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/usn-declaration/xml |
JWT; раздел «Налоги к уплате» — просмотр, право «Сдавать отчётность» |
GET |
/api/reports/personified |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/personified/file |
JWT; раздел «Налоги к уплате» — просмотр, права «Сдавать отчётность» и «Зарплаты и ПДн сотрудников» |
GET |
/api/reports/rsv |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/rsv/file |
JWT; раздел «Налоги к уплате» — просмотр, права «Сдавать отчётность» и «Зарплаты и ПДн сотрудников» |
GET |
/api/reports/ndfl6 |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/ndfl6/file |
JWT; раздел «Налоги к уплате» — просмотр, право «Сдавать отчётность» |
GET |
/api/reports/efs1 |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/reports/efs1/file |
JWT; раздел «Налоги к уплате» — просмотр, право «Сдавать отчётность» |
Без права «Зарплаты и ПДн сотрудников» ФОТ в P&L, прогнозе ДДС и оценке налога
— общей суммой, как у всех (решение владельца), в counterparties выплаты
сотрудникам сведены в строку «Сотрудники» без ИНН, в missing-docs у перевода
сотруднику нет имени и назначения — те же правила, что в /api/transactions.
GET /api/reports/tax-estimate?year&quarter — оценка налога за квартал. Суммы
incomeKopecks, expenseKopecks, contributionsKopecks — за квартал, из
налогового регистра (тот же источник, что у КУДиР и декларации). Границы
квартала — в часовом поясе компании. На УСН налог считается нарастающим итогом
с 1 января: ytd — расчёт с начала года (taxKopecks — налог с начала года,
priorAdvancesKopecks — авансы прошлых кварталов, из закрытых периодов или
пересчётом, dueKopecks — разница, меньше нуля — переплата, annual — IV
квартал, расчёт за год с минимальным налогом), taxBaseKopecks — база с начала
года, usnTaxKopecks — УСН к уплате за квартал, taxKopecks — она же плюс НДС
квартала. НДС на УСН (vatKopecks) — по первому событию (ст. 167 НК РФ): с отгрузки
по выставленному документу продажи счёта или с аванса, с вычетом НДС с аванса при
отгрузке и при возврате; поступление без счёта — как оплата с отгрузкой в тот же
день; отрицательный НДС — к возмещению, в taxKopecks не входит. На ОСНО ytd: null, roughEstimate: true и
closeNeedsConfirmation: true: оценка — грубый ориентир по оплатам, закрывать
по ней квартал без сверки нельзя. «Возврат покупателю» (refund_out)
уменьшает доход квартала возврата, «Возврат от поставщика» (refund_in) — расходы.
GET /api/reports/missing-docs — расходы, по которым не хватает закрывающего
документа или основания. Параметры: from, to (ГГГГ-ММ-ДД), limit.
В список не попадают виды платежей, у которых закрывающего документа не
бывает (needsClosingDocs): налоги, взносы, зарплата, займы, дивиденды,
переводы между своими счетами. Платёж без вида попадает — вид ещё не
проставлен, а документ по нему, скорее всего, нужен. Расход считается
закрытым актом, накладной, УПД или чеком; счёт на оплату расход не
закрывает.
GET /api/reports/unclosed-invoices — обратная сторона того же контроля:
оплаченные и частично оплаченные счета, по которым мы не выставили акт,
накладную или УПД. Параметры: from, to (ГГГГ-ММ-ДД). Период — по дате
последней оплаты в часовом поясе компании. Закрывают счёт только выставленные
документы продажи (issued): черновик и аннулированный документ не в счёт.
Остаток unclosedKopecks — оплачено минус закрыто документами, поэтому у
частично оплаченного счёта закрывать нужно только оплаченную часть; счёт,
закрытый на полную оплаченную сумму, в ответ не попадает. Ответ: items
(invoiceId, number, counterpartyName, paidKopecks, closedKopecks,
unclosedKopecks, paidOn, …), byCounterparty (count и amountKopecks
по контрагенту, по убыванию суммы) и total — «не хватает N документов на X».
Сдача отчётности — /api/filings
Отправка сформированных форм через оператора (Saby) и журнал попыток.
Единица — отправка (filing), а не период: у квартала бывает несколько
отправок и корректировок (FilingSubmission, tenant-скоуп, коллекция
filing_submissions). Реализованы проверка файла без сети и создание комплекта
в Saby при включённых разрешениях. Подписывает и отправляет человек в Saby.
| Метод | Путь | Доступ |
|---|---|---|
POST |
/api/filings |
JWT; раздел «Налоги к уплате» — просмотр, право «Сдавать отчётность»; для personified/rsv ещё «Зарплаты и ПДн сотрудников» |
GET |
/api/filings |
JWT; раздел «Налоги к уплате» — просмотр |
GET |
/api/filings/:id |
JWT; раздел «Налоги к уплате» — просмотр |
POST /api/filings — тело {kind, year?, quarter|month, dryRun?}, где kind
— personified | rsv | ndfl6 | efs1 | tax-notice: месячные personified и
tax-notice требуют month: ГГГГ-ММ, квартальные — year и quarter: 1…4.
tax-notice — уведомление КНД 1110355, тот же файл, что в /api/ens/notice/file.
dryRun: true
собирает реальный файл теми же генераторами, что и выгрузка отчётов, и без
сети возвращает FilingDryRunDto: реквизиты файла (fileName, knd,
formatVersion, encoding, sizeBytes, sha256, containsPii), метаданные будущего
комплекта (kit.type/subType/note — тип и подтип по справочнику Saby) и
warnings. Без dryRun — реальная отправка: 201 {submission} — комплект
создаётся в кабинете Saby (СБИС.ЗаписатьКомплект), подписывает и отправляет
его руководитель в Saby (MVP «черновик + подпись в Saby», план §3.4).
Замки до сети: отправка выключена в окружении (SABY_REPORTING_WRITE_ENABLED)
— 409; у компании нет возможности reports_saby_write — 403; Saby не
подключён — 409; за период уже есть активная отправка — 409; не заполнены
реквизиты (код ИФНС, регномер СФР) — 409. Отказ Saby: не хватает тарифа —
402, слишком частые вызовы — 429, прочие сетевые — 502 (попытка в
журнале помечается failed с текстом ошибки). Отправка — только для людей,
ключ приложения — 403. Блокеры формы —
409 (незакрытые месяцы и т.п.), отказ такой же, как у /reports/*/file;
для форм с ПДн без права «Зарплаты и ПДн сотрудников» — 403.
GET /api/filings?kind&status&year&quarter|month&page&limit — журнал
отправок: {items, total}, где items — FilingSubmissionDto (статус с
русской подписью, сырой код состояния оператора, события, sha256 файла,
счётчик квитанций), total — всего отправок под фильтр. status фильтрует
по статусу (prepared, created, sent, delivered, accepted,
rejected, needs_correction, unknown, failed); page (с 1) и limit
(по умолчанию 50, до 200) листают список, новые отправки сверху.
GET /api/filings/:id —
одна отправка; нет такой — 404. Файлы квитанций и статусная синхронизация
появятся на этапе R2.
Документы продажи — /api/sales-documents
Акт, товарная накладная (ТОРГ-12) и УПД. Создаются из выставленного счёта: позиции и снимки сторон копируются, основание — договор счёта, а если его нет — сам счёт. У каждого вида своя сквозная нумерация: акт № 5 и накладная № 5 — норма, два акта № 5 — потерянный документ.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/sales-documents |
JWT; раздел «Счета и продажи» — просмотр |
POST |
/api/sales-documents |
JWT; раздел «Счета и продажи» — изменение |
GET |
/api/sales-documents/:id |
JWT; раздел «Счета и продажи» — просмотр |
PATCH |
/api/sales-documents/:id |
JWT; раздел «Счета и продажи» — изменение |
DELETE |
/api/sales-documents/:id |
JWT; раздел «Счета и продажи» — управление |
POST |
/api/sales-documents/:id/issue |
JWT; раздел «Счета и продажи» — изменение, право «Выставлять счета и документы» |
POST |
/api/sales-documents/:id/cancel |
JWT; раздел «Счета и продажи» — управление, право «Выставлять счета и документы» |
POST |
/api/sales-documents/:id/send-email |
JWT; раздел «Счета и продажи» — изменение, право «Отправлять письма» |
PATCH |
/api/sales-documents/:id/exchange |
JWT; раздел «Счета и продажи» — изменение |
POST |
/api/sales-documents/:id/document |
JWT; раздел «Счета и продажи» — изменение |
GET |
/api/sales-documents/:id/print |
JWT; раздел «Счета и продажи» — просмотр |
GET |
/api/sales-documents/:id/pdf |
JWT; раздел «Счета и продажи» — просмотр |
POST /api/sales-documents принимает invoiceId, kind (act, waybill,
upd), необязательные issuedOn (ГГГГ-ММ-ДД), comment и items —
частичная отгрузка: [{ "index": 0, "quantityMilli": 4000 }], где index —
номер позиции счёта с нуля. Закрыть больше, чем в счёте, по одной позиции
нельзя (400); закрыть больше по сумме документов — можно, но ответ
issue вернёт warning.
Фильтры списка: kind, status (draft, issued, cancelled),
invoiceId, counterpartyId, from, to, q, awaiting=1 — те, что ещё
ждут подписи.
У каждого документа есть отметка о подписи exchange: mode — edo (через
ЭДО), paper (живая подпись) или none (подпись не нужна); sentAt — когда
отправили, signedAt — когда подписан в ЭДО или вернулся подписанный
экземпляр; url — ссылка на документ в ЭДО; note — чем отправляли бумагу.
Способ подставляется от контрагента (exchange в его карточке): с кем
работаем через ЭДО, тому документ и уходит в ЭДО. Меняется отдельным
маршрутом PATCH /api/sales-documents/:id/exchange — он работает и у
выставленного документа: содержание менять нельзя, а путь до подписи меняется
каждый день. Отметка «подписан» без отправки проставляет и отправку;
mode: "none" стирает обе даты.
Правится и удаляется только черновик: у выставленного есть номер, и он часть
истории — такой документ аннулируют (cancel с обязательной причиной).
Аннулирование номер в серию не возвращает.
Экземпляр в библиотеке. Выставленный документ получает экземпляр в
библиотеке (documentId) — документ /api/documents с домом
sales_document, связями с документом продажи, счётом и договором. В нём
хранятся подписанный скан (POST /api/documents/:id/files) и место
оригинала. Реквизиты идут в одну сторону: номер, дата, покупатель, сумма и
НДС — из документа продажи; аннулирование ставит экземпляру метку
«Аннулирован». Подпись остаётся у документа продажи (exchange), у
экземпляра exchange.mode — none. Правка реквизитов, подписи, дома или
замена связей у экземпляра через PATCH /api/documents/:id — 409, как и
отвязка от своего документа продажи. PDF в экземпляре не хранится — его отдаёт
GET /api/sales-documents/:id/pdf. POST /api/sales-documents/:id/document
заводит экземпляр, если его нет (удалили или документ выставлен раньше, чем
экземпляры появились), и возвращает его; у черновика — 409. Уже
выставленным экземпляры заводит миграция при старте.
send-email отправляет выставленный документ покупателю письмом с PDF во вложении: тело { to, subject?, message? } — как у счёта, до 5 адресов, без темы и текста уходит письмо по шаблону («подписать и вернуть экземпляр»). Черновик и аннулированный — 409, кривой адрес — 400, почта не настроена — 503, почтовый сервер отказал — 502. Отправка пишется в журнал документа записью «отправлен письмом: адреса» и отметку exchange не меняет.
print отдаёт HTML для печати из браузера, pdf — тот же документ файлом
(?download=1 — вложением). Формы: акт — свободная с обязательными
реквизитами, ТОРГ-12 и УПД — по утверждённым формам, в альбомной
ориентации. УПД с НДС — статус 1, он же счёт-фактура (/api/vat-invoices):
строки 5 и 5б печатаются из зачёта авансов.
Чем закрыт счёт — GET /api/invoices/:id/documents: документы по счёту,
closedKopecks (сумма выставленных) и uncoveredKopecks (остаток).
Счета-фактуры — /api/vat-invoices
Исходящие счета-фактуры плательщика НДС: отгрузочные (shipment), на аванс (advance) и корректировочные (correction, КСФ). Формы — ПП 1137 в ред. ПП № 26 (с 01.04.2026): прил. 1 и прил. 2. Разбор правил — docs/research/vat-ledgers.md.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/vat-invoices |
JWT; раздел «Счета и продажи» — просмотр |
GET |
/api/vat-invoices/settings |
JWT; раздел «Счета и продажи» — просмотр |
PATCH |
/api/vat-invoices/settings |
JWT; раздел «Счета и продажи» — управление |
POST |
/api/vat-invoices/advances/sync |
JWT; раздел «Счета и продажи» — изменение |
POST |
/api/vat-invoices/corrections |
JWT; раздел «Счета и продажи» — изменение |
GET |
/api/vat-invoices/:id |
JWT; раздел «Счета и продажи» — просмотр |
PATCH |
/api/vat-invoices/:id |
JWT; раздел «Счета и продажи» — изменение |
DELETE |
/api/vat-invoices/:id |
JWT; раздел «Счета и продажи» — управление |
POST |
/api/vat-invoices/:id/issue |
JWT; раздел «Счета и продажи» — изменение, право «Выставлять счета и документы» |
POST |
/api/vat-invoices/:id/annul |
JWT; раздел «Счета и продажи» — управление, право «Выставлять счета и документы» |
POST |
/api/vat-invoices/:id/refunds |
JWT; раздел «Счета и продажи» — изменение, право «Выставлять счета и документы» |
POST |
/api/vat-invoices/:id/revisions |
JWT; раздел «Счета и продажи» — изменение |
GET |
/api/vat-invoices/:id/print |
JWT; раздел «Счета и продажи» — просмотр |
GET |
/api/vat-invoices/:id/pdf |
JWT; раздел «Счета и продажи» — просмотр |
Откуда берутся. Отгрузочный заводится сам, когда выставлен документ продажи с НДС (хоть одна позиция не «без НДС»). УПД статуса 1 — это и есть счёт-фактура: запись выставлена сразу, isUpd: true, номер и дата — УПД, позиции (lines) читаются из УПД, печать отдаёт бланк УПД. К акту и накладной — отдельный счёт-фактура с номером из серии счетов-фактур: при настройке primaryDocument: "separate" он выставляется вместе с документом, при "upd" (по умолчанию) остаётся черновиком со сроком dueOn. Аннулирование документа продажи аннулирует его счёт-фактуру (черновик — удаляет). Выставить или аннулировать счёт-фактуру по УПД отдельно нельзя — 409.
Авансы. На каждое поступление по счёту, пришедшее раньше отгрузки, заводится черновик счёта-фактуры на аванс (код 02): строки по ставкам счёта с расчётной ставкой (22/122, 10/110, 7/107, 5/105), в строке 5 (paymentDocs) — номер п/п из выписки и дата, dueOn — дата поступления + 5 дней (п. 3 ст. 168 НК). Позиции без НДС и 0% в аванс не входят; переплата сверх счёта — тоже. Отгрузки зачитывают авансы по порядку поступления (FIFO): зачёт пишется в отгрузочный счёт-фактуру — advanceLinks (строка 5б) с НДС в доле зачтённого и paymentDocs (строка 5). При skipSameQuarterAdvances: true (по умолчанию, МР ФНС п. 13) черновик на часть, зачтённую отгрузкой того же квартала, не нужен: полностью зачтённый снимается, частично — уменьшается. Выставленный не пересчитывается. У авансового в DTO advance: coveredKopecks, paymentKopecks, зачёты offsets, возвраты refunds, остаток remainingKopecks и remainingVatKopecks. Синхронизация идёт после выставления и аннулирования документов продажи, раз в час в фоне и по POST /api/vat-invoices/advances/sync (тело {} — счета с оплатами за полгода, { "invoiceId" } — один счёт; ответ { created, updated, removed }). Поступления без счёта (только по договору) пока не разбираются.
Номера. Выдаются при выставлении, черновик номер не тратит; неудачное сохранение номер возвращает — нумерация без дыр. Серии: vat_invoice — отгрузочные к актам и КСФ, vat_advance — авансовые, с префиксом А-; счётчик с начала года. Номер УПД статуса 1 — из серии УПД. Серии счетов-фактур в /api/invoice-series не показываются. Если в серии уже есть документ с более поздней датой и меньшим номером, issue вернёт warning о нарушенной хронологии.
Фильтры списка: kind, status (draft, issued, annulled), invoiceId, salesDocumentId, counterpartyId, quarter (ГГГГ-Qn), from, to, q, due=1 — черновики со сроком по возрастанию срока. Заменённые исправлением скрыты; all=1 — показать все.
PATCH /:id — только черновик: issuedOn (кроме отгрузочного и исправления), comment, paymentDocs: [{ number, date }] (авансовый: номер п/п, если банк его не прислал — без номера issue отвечает 400), counterparty: { name, inn, kpp, legalAddress } (только исправление). Удаляются (DELETE) только черновики КСФ и исправлений; черновики авансовых и отгрузочных снимает система — 409.
POST /corrections — КСФ к действующему выставленному отгрузочному: { vatInvoiceId, date, items: [{ index, quantityMilli, priceKopecks }], comment? }, index — строка исходного с нуля. В КСФ попадают изменённые строки (correctionLines: before — строка А, after — Б), итоги increase (В) и decrease (Г); operationCode — 01 при увеличении, 18 при уменьшении. Ничего не изменилось — 400.
POST /:id/revisions — исправление (строка 1а): { date }, черновик-копия с тем же номером и датой, revision: { number, date }, покупатель — из текущей карточки. При выставлении исходный получает revisedById. УПД так не исправляется — 409.
POST /:id/refunds — возврат аванса по выставленному авансовому: { date, amountKopecks, documentNumber?, transactionId? }, не больше остатка (400); НДС с возвращённой части — к вычету по коду 22. Авансовый с зачётом или возвратом не аннулируется — 409.
Настройки (/settings): primaryDocument — upd или separate; skipSameQuarterAdvances; advanceReminders — письмо владельцам за день до срока выставления и в день срока.
print — HTML, pdf — файл (?download=1 — вложением). Факсимиле в счёте-фактуре не ставится (п. 6 ст. 169 НК). Выставление и аннулирование в закрытом квартале — как у документов продажи.
Книги НДС — /api/vat-ledgers
Книга продаж и книга покупок за квартал по формам прил. 5 и 4 ПП 1137 в ред. ПП № 26 (vat-ledgers.md T-V5–T-V9, T-V12). Пока квартал открыт, книга собирается из документов на лету — «на конец квартала»: аннулирование или исправление после конца квартала в эту книгу не входит. Закрытый квартал (есть PeriodClose по scope=tax) отдаётся снимком на закрытие — хранимой книгой (stored); изменения документов закрытого квартала (сделанные во время корректировки) видны в дополнительном листе stored.supplement, а stored.needsAmendedDeclaration говорит, что по кварталу нужна уточнённая декларация (п. 1 ст. 81 НК).
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/vat-ledgers |
JWT; раздел «Налоги» — просмотр |
GET |
/api/vat-ledgers/pdf |
JWT; раздел «Налоги» — просмотр, право «Выгружать данные» |
GET |
/api/vat-ledgers/xlsx |
JWT; раздел «Налоги» — просмотр, право «Выгружать данные» |
GET |
/api/vat-ledgers/declaration |
JWT; раздел «Налоги» — просмотр, право «Выгружать данные» |
Квартал — в query: ?quarter=2026-Q3; без него или в другом виде — 400.
XML декларации (GET /api/vat-ledgers/declaration, формат 5.12 по приказу ЕД-7-3/989@ в ред. ЕД-7-3/1227@): какой файл — в query ?file=: NO_NDS (титульный лист, раздел 1 — к уплате, раздел 3 — из книг), NO_NDS_9 (книга продаж), NO_NDS_8 (книга покупок); файла с нулевой книгой в пакете нет. Для закрытого квартала с изменениями — ?correction=1: NO_NDS_91 и NO_NDS_81 (приложения 1 — дополнительные листы, НомКорр=1); без закрытия — 409, без такого файла в пакете — 404. Файл — в windows-1251, ИдФайл совпадает с именем файла без расширения; в Content-Disposition — имя по шаблону NO_NDS…_КодНО_КодНО_ИНН+КПП_ГГГГММДД_ЧЧММСС.xml.
Книга продаж (sales.rows, графы 1–19): 01 — выставленные отгрузочные счета-фактуры (УПД статуса 1 и к актам) с датой в квартале, гр. 11а (advanceInvoices) — зачтённые авансовые; 02 — авансовые квартала поступления оплаты: гр. 11 (paymentDocs) — платёжка, гр. 13б — сумма, НДС по расчётной ставке в гр. 17–18б, гр. 14–16 пустые; 01 с гр. 5 (correction) — КСФ на увеличение. Суммы по ставкам — base (гр. 14, 14а, 15, 15а, 15б, 16) и vat (гр. 17, 17а, 18, 18а, 18б) с ключами 22, 20, 10, 7, 5, 0; итоги — sales.totals, НДС всего — sales.vatKopecks. Позиции «без НДС» в книгу не регистрируются.
Книга покупок (purchases.rows, графы 1–15): 22 — вычет НДС с аванса при отгрузке (гр. 3 — авансовый, гр. 7а shipmentInvoices — отгрузочный, в квартале отгрузки) и при возврате аванса (гр. 7 paymentDocs — наша платёжка); 18 — наша КСФ на уменьшение (гр. 9–10 — наши данные); 01 — входящие УПД статуса 1 и счета-фактуры из реестра документов (purchaseInvoice) с кварталом вычета (заявленным или по дате принятия на учёт), для облагаемых операций. Входящие идут в книгу, только если на конец квартала компания принимает входной НДС к вычету — ОСНО или УСН с общей ставкой (inputVatDeductible); на УСН 5% и 7% — только коды 22 и 18. Итог гр. 15 — purchases.vatKopecks.
У строки source — откуда она: { kind: "vat_invoice" | "library_document", id }. payableKopecks — книга продаж минус книга покупок. warnings — что не попало и почему: невыставленные счета-фактуры квартала, зачёт по черновику аванса, входящие без номера или даты принятия, входящие на УСН 5/7. Также vatPayer, regimeLabel («ОСНО», «УСН; НДС 5% без вычетов»), seller, from, to.
pdf — книги по форме для бухгалтера и подписи руководителя: без book — обе одним файлом, book=sales или book=purchases — одна. xlsx — два листа с теми же графами.
Настройки компании — /api/settings
Реквизиты компании проверяются так же, как у контрагента; тип — по ИНН: 10 цифр — ООО, 12 — ИП (у ИП нет КПП). Ошибка — 400 с fields.
Для печатной формы счёта: phone и email печатаются в строке «Поставщик», invoiceTerms — текст условий под сроком оплаты (у каждой компании свой), directorName и accountantName — подписи.
paymentPollMinutes — как часто «Поток» сам забирает выписку и разносит оплаты: 5, 15, 30 или 60 минут, 0 — только вручную (по умолчанию 15).
sfrRegNumber — регистрационный номер в СФР (10 цифр, как в письме фонда о регистрации страхователя; пусто — снять): нужен для ЕФС-1 — без него форма не собирается, а отправка отчётности в Saby отвечает отказом. Для форм ФНС (6-НДФЛ, РСВ, персонифицированные) не используется.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/settings |
JWT; любой член компании |
PATCH |
/api/settings |
JWT; право «Настройки компании» |
Платежи (выписка) — /api/transactions
Платежи приезжают из выписки банка и не создаются через API — только читаются и размечаются.
Поиск q ищет по назначению и контрагенту, а строка, похожая на сумму (52 300, 52300,50), — ещё и платежи ровно на эту сумму. Кусок числа вроде 52 суммой не считается.
Без права «Зарплаты и ПДн сотрудников» у выплат сотрудникам — вид платежа с ролью salary или получатель, совпавший по имени с сотрудником, — counterpartyName и purpose заменены на «Выплата сотруднику» («Поступление от сотрудника» для входящих), counterpartyInn — null, comments — пустой список (в тексте комментария бывает фамилия). Сумма, дата и вид остаются. Так в списке, карточке, ответах на комментарии и в export.csv. Текстом такие платежи не находятся (иначе поиск по фамилии выдал бы сумму), по сумме — находятся, скрытыми. Та же подпись — в акте сверки, в списке платежей договора, в неразнесённых поступлениях и в подписи связи документа с платежом.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/transactions |
JWT; раздел «Платежи» — просмотр |
GET |
/api/transactions/:id |
JWT; раздел «Платежи» — просмотр |
PATCH |
/api/transactions/:id |
JWT; раздел «Платежи» — изменение |
POST |
/api/transactions/:id/comments |
JWT; раздел «Платежи» — просмотр |
DELETE |
/api/transactions/:id/comments/:commentId |
JWT; раздел «Платежи» — просмотр |
GET |
/api/transactions/export.csv |
JWT; раздел «Платежи» — просмотр, право «Массовые выгрузки» |
POST |
/api/transactions/recategorize |
JWT; раздел «Платежи» — изменение |
PATCH принимает, кроме вида, категории и признаков учёта, payrollMonth
(«ГГГГ-ММ», null снимает пометку) — «за какой месяц» у выплаты зарплаты:
только у платежей вида с ролью/кодом salary (400 у прочих). Дата такой
операции заменяет плановую дату выплаты из графика при раскладке НДФЛ этого
месяца; если месяц уже закрыт — раскладка не пересчитается, ответ несёт
warnings со строкой об этом (пометка пригодится при открытии периода или
корректировке). payrollCorrection (boolean) — помеченная «за месяц» операция
не выплата, а корректировка (повторная отправка вернувшейся зарплаты,
исправительная проводка): в раскладку НДФЛ не входит и плановую выплату не
замещает. true без payrollMonth — 400; снятие payrollMonth снимает и
признак. В DTO операции — поле payrollCorrection.
Вебхуки — /api/webhooks
Адреса, на которые «Поток» шлёт события компании: invoice.issued, invoice.paid, invoice.partially_paid, invoice.payment_removed, invoice.cancelled, payment.received, document.issued, document.cancelled, а также события конвейера автоматизации automation.task.ready (задача конвейера готова к действию человека) и automation.task.failed (задача конвейера упала). Настраивает администратор компании или ключ приложения со скоупом webhooks:write. Секрет подписи показывается один раз — при создании и смене (rotate-secret). test шлёт событие ping сразу и возвращает итог попытки; deliveries — последние 50 доставок с кодом ответа и временем повтора.
Каждое событие приходит POST-запросом с телом { id, type, createdAt, data } и заголовками X-Potok-Event (тип), X-Potok-Delivery (id доставки, при повторе тот же) и X-Potok-Signature (t=<unix-время>,v1=<HMAC-SHA256 секретом от «t.тело»>). В data у событий invoice.* — invoice (счёт целиком, как в GET /api/invoices/:id), у payment.received — payment (id, bankAccountId, date, amountKopecks, counterpartyName, counterpartyInn, purpose), у ping — message. Не 2xx за 10 секунд — повтор через 1, 5, 30 минут, 2, 6, 12 и 24 часа. Адрес — только https и не во внутреннюю сеть; переадресации не выполняются. Подробно, с проверкой подписи, — в руководстве по интеграции.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/webhooks |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
POST |
/api/webhooks |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
PATCH |
/api/webhooks/:id |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
DELETE |
/api/webhooks/:id |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
POST |
/api/webhooks/:id/rotate-secret |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
POST |
/api/webhooks/:id/test |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
GET |
/api/webhooks/:id/deliveries |
JWT с правом «Интеграции» или ключ со скоупом webhooks:write |
Интеграция Saby Docs — /api/saby
Подключение кабинета Saby (ЭДО Тензора) к компании: сервисным ключом приложения (рекомендуется, без SMS; выпускается в Saby в «Настройки → Безопасность → Подключения к Saby») или логином и паролем пользователя Saby — владельцы рекомендуют завести отдельного сотрудника «для Потока» без подтверждения входа по SMS. Если SMS у пользователя включено, подключение встаёт в статус needs_sms: Saby шлёт код, человек вводит его на маршруте sms-code (ОтправитьКодАутентификации/ПодтвердитьВход). Пароль, секрет ключа и сессии хранятся зашифрованными AES-256-GCM и в ответах API не возвращаются никогда; секреты способа входа, которым не пользуются, при подключении стираются. При подключении сразу выполняется вход: неверные данные подключение не сохраняют (400), нехватка тарифа «Обмен с контрагентами» — 402. Подключённая компания опрашивается фоном (saby-sync) по курсору СписокИзменений, при первом подключении — с начала текущего квартала; входящие документы заводятся в «Документах» черновиками (recognitionDraft) с полями из XML УПД 5.03 (XSD — nalog.gov.ru, schemas/), оригинальным XML и подписями вложения; исходящие (черновики, записанные из документов продаж) приходят той же синхронизацией и сопоставляются по exchange.externalId — отметки «отправлен» и «подписан контрагентом» ставятся сами. Режим «только чтение» включён по умолчанию для всех вызовов Saby. Интеграция входит во все тарифы; при выключении флага тарифа маршруты подключения и синхронизации отвечают 403. Маршруты изменения — только для людей: ключ приложения со скоупом webhooks:write интеграцию менять не может (403). connect и sms-code ограничены по частоте на компанию: 429 после 5 (соответственно 10) попыток за 10 минут.
Контур записи (срез S5). Запись в Saby — проверка контрагента (ИнформацияОКонтрагенте заводит его карточку, если его там нет), приглашение и черновик исходящего — закрыта двумя выключателями сразу: окружением SABY_WRITE_ENABLED (по умолчанию false) и возможностью компании edo_saby_write (по умолчанию выключена у всех, включается только явным списком возможностей; hasFeature её не выдаёт никогда — только явная проверка). Замок продублирован на уровне клиента: readOnly: false без внутреннего признака вызова из контура записи отклоняется до сети, и метод обязан быть из списка пишущих (ИнформацияОКонтрагенте, ЗаписатьДокумент, ОтправитьПриглашение). Пока запись выключена, маршруты записи не падают, а отвечают 200 { dryRun: true, request, hint } — что было бы отправлено, без секретов и без base64 вложения (только имя файла и размер). Реального запроса в Saby при этом нет ни одного. Состояние выключателей видно вебу в GET /api/saby как writeEnabled.
Контур записи отчётности (комплекты СБИС.ЗаписатьКомплект, раздел «Отчёты») — те же два замка со своими именами: окружение SABY_REPORTING_WRITE_ENABLED и возможность компании reports_saby_write. Админ компании включает её сам в этом же разделе — PATCH /api/saby/reporting тело { enabled } (обе возможности сразу: reports_saby и reports_saby_write); оба замка видны в статусе как reportingEnabled (компания) и reportingWriteEnv (окружение). Маршрут — только для людей с правом «Интеграции».
Статусы исходящих. Исходящие статусы приходят той же синхронизацией по exchange.externalId, время отметок — время события Saby, не синхронизации: 3/4 → exchange.sentAt, 7 → exchange.sentAt и exchange.signedAt, 10/23 → exchange.sentAt (документ ждёт подписи контрагента), 9 → exchange.rejectedAt «Отклонён контрагентом» с причиной в exchange.note (из фильтра «ждут подписи» документ уходит), 19/20/22 с говорящим названием — заметка об отзыве/аннулировании. Каждый документ обрабатывается один раз за запуск.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/saby |
JWT; раздел «Документы» на просмотр. Статус подключения без секретов: available (включено тарифом), writeEnabled (запись в Saby реально открыта: окружение и возможность edo_saby_write компании — иначе интерфейс не обещает отправку), reportingEnabled (возможность компании reports_saby_write — отправка отчётности) и reportingWriteEnv (серверный SABY_REPORTING_WRITE_ENABLED; отправка работает, когда оба замка открыты), canManage (право «Интеграции» у текущего пользователя), connected (всё, кроме disconnected — при error кабинет подключён), stand, authMode (password/service_key), serviceKeyTail (хвост идентификатора ключа), login (только при canManage), status (connected/needs_sms/error/disconnected), phoneMask и codeExpiresAt при ожидании кода, ourOrg, syncFrom, pollMinutes, lastSyncAt, lastSyncError, lastSyncStats (created/updated/skipped/unknownTypes/failed) |
PATCH |
/api/saby/reporting |
JWT с правом «Интеграции», только человек. Тело { enabled } — включает или выключает у компании отправку отчётности (reports_saby + reports_saby_write); ответ { reportingEnabled, reportingWriteEnv }. Серверный замок SABY_REPORTING_WRITE_ENABLED этим маршрутом не трогается — без него отправка из «Налогов» отвечает 409, даже когда возможность включена |
POST |
/api/saby/connect |
JWT с правом «Интеграции». Тело { authMode?, login?, password?, serviceKeyId?, serviceKeySecret?, accountNumber?, stand?, syncFrom?, pollMinutes? } — либо пароль (логин обязателен), либо пара ключа (логин не нужен); ответ — статус, как GET. При двухфакторке — статус needs_sms с маской телефона; 429 — чаще 5 попыток за 10 минут с компании |
POST |
/api/saby/sms-code |
JWT с правом «Интеграции». Тело { code } — подтверждение входа; 409, если код не запрашивался; 429 — чаще 10 попыток за 10 минут |
POST |
/api/saby/sms-code/resend |
JWT с правом «Интеграции». Просить Saby отправить код ещё раз; 409, если идентификатор подтверждения истёк |
POST |
/api/saby/sync |
JWT с правом «Интеграции». «Забрать сейчас»: не чаще раза в минуту (429); 409, если не подключено или не введён код из SMS |
DELETE |
/api/saby |
JWT с правом «Интеграции». Отключить: выход из кабинета, стирание пароля и сессии; документы остаются. 404, если подключения нет |
POST |
/api/saby/counterparties/:id/check |
Только JWT (человек); раздел «Контрагенты» на изменение. Проверка контрагента в ЭДО (ИнформацияОКонтрагенте с ДопПоля: "СписокИдентификаторов", чтобы Saby вернул ящики): ЮЛ — СвЮЛ с ИНН и КПП, ИП — СвФЛ по 12-значному ИНН. Тело { force? } — «Проверить ещё раз»; без него результат живёт на карточке сутки (cached: true), повторный вопрос Saby не зовёт. Ответ { edo: { checkedAt, state: saby|roaming|invited|none|refused|unknown, operator, participantId, boxes[], invitedAt }, cached }. Незнакомая форма ответа Saby — state: unknown с warning, кэшем не считается: повторная проверка идёт в Saby сразу. Побочный эффект Saby: нет карточки — заводится (потому маршрут и по кнопке, массово не звать). 400 — нет ИНН или физлицо; 409 — Saby не подключён; 429 — чаще 30 проверок за 10 минут с компании; при выключенной записи — { dryRun: true, request } |
POST |
/api/saby/counterparties/:id/invite |
Только JWT (человек); раздел «Контрагенты» на изменение. Приглашение к ЭДО (ОтправитьПриглашение), тело { email }. Роуминговому контрагенту передаётся ИдентификаторАЯ — идентификатор ящика из последней проверки. Ответ { ok, invitationId, edo } — на карточке пометка «Приглашён» (state: invited, invitedAt). Приглашение без свежей проверки кэш проверки не подменяет; тому, кто уже «В Saby», приглашение не нужно — 409. Статус приглашения (СписокИзмененийПриглашений) пока не отслеживается. 400 — нет ИНН, физлицо или кривой адрес; при выключенной записи — { dryRun: true, request } |
POST |
/api/saby/sales-documents/:id/send |
Только JWT (человек) с правом «Выставлять счета и документы». Черновик исходящего в Saby (ЗаписатьДокумент, регламент «Реализация») из PDF документа продаж: только выставленные акт/накладная/УПД со статусом 2. УПД со статусом 1 — 409 «передаётся только формализованным XML — появится позже»; уже подписанный (любой режим) или уже отправленный через ЭДО — 409, повтор по черновику без отметок об отправке разрешён (тот же идентификатор). Идентификатор черновика — стабильный uuid документа: повтор перезаписывает тот же черновик. Перед записью контрагент проверяется (кэш или вызов); не в ЭДО — не блокирует, в ответе warning. Ответ { document } с exchange: { mode: edo, url, externalId, system: saby, draftAt } — ссылка ведёт на подписание в Saby. 409 — черновик или аннулирован; 429 — чаще 20 отправок за 10 минут с компании; при выключенной записи — { dryRun: true, request } без base64 вложения |
Пользователи — /api/users
Сотрудники компании запроса и их роли в ней. Обычный путь добавить сотрудника — приглашение (/api/invites); POST /api/users с временным паролем — запасной, когда почта недоступна. PATCH меняет роль только в этой компании; имя человека, который работает и в других компаниях, отсюда не меняется (409). DELETE убирает человека из компании; учётная запись удаляется, только если других компаний у него нет. Email, уже занятый на платформе, — 409.
| Метод | Путь | Доступ |
|---|---|---|
GET |
/api/users |
JWT; право «Управление пользователями» |
POST |
/api/users |
JWT; право «Управление пользователями» |
DELETE |
/api/users/:id |
JWT; право «Управление пользователями» |
PATCH |
/api/users/:id |
JWT; право «Управление пользователями» |