REST API

Apiin построен по принципу API-first: любое действие в кабинете — это вызов публичного REST API. Тот же контракт доступен внешним интеграциям и MCP-серверу для нейросетей.

Аутентификация

Ключи создаются в кабинете, в разделе API и MCP, — программно ключ выпустить нельзя. Каждому ключу выдаётся набор прав (scopes) и срок действия (постоянный или до даты). Полный ключ имеет вид apiin_live_… и передаётся в заголовке:

Authorization: Bearer apiin_live_xxxxxxxx

Один ключ может работать с несколькими организациями (набор задаётся при создании). Базовый адрес — https://api.apiin.ru. Лимит — 600 запросов в минуту на ключ; при превышении приходит 429. Ключ можно отозвать в любой момент.

Права (scopes)

Действие требует соответствующего права; * — все права:

  • counterparties:read / counterparties:write — контрагенты;
  • documents:read / documents:write — документы, PDF, оплаты, подписи, связки;
  • orders:read / orders:write — заказы;
  • public_links:write — публичные ссылки;
  • send:write — отправка на email;
  • templates:read / templates:write, assets:write, offers:write;
  • organizations:read / organizations:write;
  • webhooks:write — настройка вебхуков.

Организации

Все ресурсы скоупятся по организации: /api/organizations/{org_id}/…. Доступные ключу организации возвращает GET /api/whoami:

GET https://api.apiin.ru/api/whoami
Authorization: Bearer apiin_live_xxxxxxxx

→ { "organization_id": "<первая>", "organization_ids": ["<uuid>", …] }

Контрагент

Документ выставляется существующему контрагенту, поэтому его создают заранее. Обязательны kind (ooo|ip|self_employed|individual) и name; остальные реквизиты — по желанию. Данные по ИНН можно подтянуть: GET /api/dadata/party?inn=….

POST https://api.apiin.ru/api/organizations/{org_id}/counterparties
Authorization: Bearer apiin_live_xxxxxxxx
Content-Type: application/json

{
  "kind": "ooo",
  "name": "ООО «Ромашка»",
  "inn": "7707083893",
  "kpp": "770701001"
}

В ответе — контрагент с полем id (UUID); его подставляют в счёт.

Счёт

Цены — целое число в копейках (45 000 ₽ → 4500000). Обязательны kind, counterparty_id и хотя бы одна позиция. Номер присваивается автоматически по формату нумерации организации.

POST https://api.apiin.ru/api/organizations/{org_id}/documents
Authorization: Bearer apiin_live_xxxxxxxx
Content-Type: application/json

{
  "kind": "invoice",
  "counterparty_id": "<uuid контрагента>",
  "vat_mode": "none",
  "with_qr": true,
  "items": [
    { "name": "Консультация", "quantity": 1, "price": 4500000 }
  ]
}

vat_mode: none (без НДС) | usn | osno. В ответе — документ с id, номером и рассчитанными суммами (subtotal, vat_amount, total).

PDF, Word и публичная ссылка

  • PDF: GET …/documents/{id}/pdf (добавьте ?download=1 для вложения);
  • Word: GET …/documents/{id}/docx;
  • Публичная ссылка на счёт: POST …/public-links с телом { "target": "document", "target_ref_id": "<id счёта>" } — в ответе url неугадываемой страницы (с QR и реквизитами).

Оплата, акт, подпись

  • Отметить оплату полностью: POST …/documents/{id}/mark-paid; частичную — POST …/documents/{id}/payments с { "amount": <копейки>, "paid_at": "ГГГГ-ММ-ДД" };
  • Акт на основе счёта одним вызовом: POST …/documents/{id}/create-act — позиции и контрагент копируются из счёта, основание проставляется «По счёту № … от …», связка «счёт ↔ акт» создаётся автоматически. Повторный вызов вернёт уже созданный акт (без дублей);
  • Подписать акт: POST …/documents/{id}/sign. Связка закрывается сама, когда счёт оплачен, а акт подписан.

Вебхуки

Чтобы узнавать об оплате и подписи без опроса, настройте вебхук — один адрес на организацию (право webhooks:write, роль admin или owner):

PUT https://api.apiin.ru/api/organizations/{org_id}/webhook
Authorization: Bearer apiin_live_xxxxxxxx
Content-Type: application/json

{
  "url": "https://example.com/apiin-webhook",
  "events": ["document.payment_status_changed", "document.sign_status_changed"]
}

События:

  • document.created — создан документ;
  • document.payment_status_changed — статус оплаты (unpaid|partial|paid);
  • document.sign_status_changed — статус подписи акта.

Каждое событие — POST на ваш адрес с телом, где есть event, occurred_at, organization_id и document (id, kind, number, counterparty_id, total, payment_status, sign_status). Заголовки запроса:

  • X-Apiin-Event — имя события;
  • X-Apiin-Delivery — UUID доставки (используйте для идемпотентности приёмника);
  • X-Apiin-Signature: sha256=<hex> — HMAC-SHA256 тела на секрете вебхука (секрет возвращается в ответе PUT/GET …/webhook). Проверяйте подпись, прежде чем доверять телу.

Доставка считается успешной при ответе 2xx за 10 секунд. Иначе — повторы с увеличивающейся паузой (до 6 попыток за ~9 часов), после чего доставка помечается несостоявшейся. Журнал: GET …/webhook/deliveries; тестовое событие: POST …/webhook/test.

Адрес вебхука должен быть публичным https на порту 443. Локальные и приватные адреса отклоняются — для локальной отладки используйте туннель (например, ngrok).

Списки: пагинация и синхронизация

Списки документов и контрагентов принимают необязательные query-параметры. Без них возвращается весь список (как раньше). Для интеграций:

  • ?limit= и ?offset= — пагинация (limit от 1 до 500).
  • ?updated_since= — только изменённые с указанного момента (формат RFC3339, например 2026-07-17T10:00:00Z) — для инкрементальной синхронизации.
  • ?inn= (только контрагенты) — точный поиск по ИНН: удобно проверить, есть ли уже такой контрагент, перед созданием.

Идемпотентность (защита от дублей)

Создающие запросы POST /documents и POST /counterparties принимают заголовок Idempotency-Key: <уникальная строка>. Первый запрос создаёт сущность и запоминает результат на 24 часа; повтор с тем же ключом вернёт ту же сущность (статус 200), а не дубль с новым номером. Если предыдущий запрос ещё выполняется — повтор получит 409. Используйте это при ретраях после сетевого таймаута: сгенерируйте ключ один раз на операцию и отправляйте его при каждой попытке.

Ошибки

Ошибки возвращаются с соответствующим HTTP-статусом (400 — неверные данные, 401 — нет/недействительный ключ, 403 — не хватает прав, 404 — не найдено или чужая организация, 409 — конфликт, 429 — превышен лимит) и телом { "error": "<описание>", "code": "<машинный код>" }. Поле code — стабильный идентификатор (например rate_limited, not_found, document_protected); в коде опирайтесь на него, а не на текст error. При 429 приходит заголовок Retry-After (секунды до повтора) и X-RateLimit-*.

Создайте первый ключ в кабинете. Для управления документами из нейросети — раздел MCP для нейросетей.