Документация API

REST API и вебхуки Aynes: авторизация, общие правила, методы с примерами запросов и ответов, события и проверка подписи.

Описание OpenAPI

Содержание

Введение

API Aynes — REST-интерфейс организации для учётных систем, CRM, систем бронирования и своих скриптов. Через него можно читать операции, заявки на оплату, справочники и отчёты, создавать заявки на оплату, передавать брони и оплаты по ним, а вебхуки сами сообщат вашей системе о событиях в Aynes — опрашивать не нужно.

  • Операции — список с фильтрами экрана «Операции».
  • Заявки на оплату — чтение с ходом согласования и создание: новая заявка сразу уходит на согласование.
  • Справочники — направления, юрлица, категории, контрагенты, счета и кассы, проекты.
  • Отчёты — ДДС и ПиУ за период, теми же числами, что на экранах.
  • Брони — передача броней и оплат из системы бронирования и сверка принятого.
  • Вебхуки — новая операция, смена статуса и изменение заявки, ошибка загрузки выписки, оплата брони найдена в выписке.
Базовый адресhttps://aynes.pro/api/v1
ПротоколТолько HTTPS
ФорматJSON в кодировке UTF-8; тело запроса — с заголовком Content-Type: application/json
СуммыЦелые числа в минимальных единицах валюты — тыйынах, центах, копейках: 12 500,50 сома — 1250050. Рядом всегда код валюты ISO 4217: KGS, USD, RUB…
ЭквивалентbaseAmount — сумма в базовой валюте организации, exchangeRate — курс к ней строкой (десятичная дробь без потери точности). Курс фиксируется в момент проведения и задним числом не меняется.
ДатыГГГГ-ММ-ДД, например 2026-10-08
Моменты времениISO 8601. В ответах — UTC (2026-10-08T09:30:00.000Z), в запросах — с любым поясом (2026-10-08T15:30:00+06:00). Календарная дата операции в ответе — полночь UTC этого дня.
Часовой поясДни и месяцы отчётов — в поясе организации: tenant.timezone в GET /me
ИдентификаторыСтроки вида cm2opr9e6k0009m1n3p5q7r9s: храните как есть, не разбирайте
Описание OpenAPI/api/v1/openapi.json — те же методы в формате OpenAPI 3.0, без токена: для генераторов клиентов и Postman

Версия API — в адресе: v1. В неё могут добавляться методы, необязательные параметры и поля ответа — разбирайте ответ, пропуская незнакомые поля. Несовместимые изменения выйдут новой версией; что менялось — в разделе «Изменения API».

Проверить токен одной командой:

bash
export AYNES_TOKEN="fsk_…"
curl "https://aynes.pro/api/v1/me" -H "Authorization: Bearer $AYNES_TOKEN"

Авторизация

Каждый запрос несёт токен организации в заголовке Authorization:

http
Authorization: Bearer fsk_…

Токен работает от имени организации; заявки, созданные через API, идут от имени того, кто токен выпустил. Храните токен на сервере — не в браузере и не в мобильном приложении. Без токена, с неверным или отозванным — 401 unauthorized.

Где выпустить

  • Настройки → API и вебхуки → Токены API — владелец или администратор организации задаёт название, скоупы и, по желанию, срок действия. Секрет fsk_… показывается один раз: в Aynes хранится только его хеш, и потерянный токен не восстановить — выпустите новый.
  • Брони → Настройки броней → «Ключ для системы бронирования» — ключ со скоупами bookings:read и bookings:write для Voyz или другой системы бронирования. Там же — адрес API, который вставляют в систему вместе с ключом.

Скоупы

Токен получает только выбранные скоупы. Метод без нужного скоупа отвечает 403 forbidden с details.required — каким скоупом его открыть. GET /me открыт любому действующему токену.

СкоупЧто открываетМетоды
operations:readЧтение операцийGET /operations
operations:writeСоздание операцийPOST /operations
payment-requests:readЧтение заявок на оплатуGET /payment-requests, GET /payment-requests/{id}
payment-requests:writeСоздание заявок на оплатуPOST /payment-requests
references:readЧтение справочниковGET /references/units, GET /references/legal-entities, GET /references/categories, GET /references/contractors, GET /references/bank-accounts, GET /references/projects
reports:readЧтение отчётовGET /reports/cashflow, GET /reports/pnl
bookings:readЧтение броней и оплат (сверка) (функция тарифа «Брони»)GET /bookings, GET /bookings/{source}/{externalId}, GET /bookings/{source}/{externalId}/payments/{paymentId}
bookings:writeПередача броней и оплат по ним (функция тарифа «Брони»)PUT /bookings/{source}/{externalId}, PUT /bookings/{source}/{externalId}/payments/{paymentId}

Тариф

  • Внешний API и вебхуки — функция тарифа «API». На тарифе без неё запрос отвечает 403 forbidden с details.feature = "api" и details.requiredPlan — названием тарифа, где функция есть.
  • Ключ, у которого все скоупы — bookings:*, требует не «API», а функцию «Брони»: систему бронирования можно подключить на любом тарифе с бронями (details.feature = "bookings").
  • Токен с любым другим скоупом требует «API» и на методах броней. Ключу броней на тарифе без «API» GET /me отвечает 403.

Срок жизни и отзыв

  • Токен действует, пока не отозван, не истёк его срок (если задан) и пока тот, кто его выпустил, — владелец или администратор организации. Выпустивший ушёл или стал обычным участником — интеграция получает 401, пока действующий администратор не выпустит новый токен.
  • Отзыв — кнопкой «Отозвать» на экране токенов или ключей броней — действует сразу: следующий запрос получит 401 unauthorized.
  • Организация в режиме чтения (подписка не оплачена или на паузе): чтение работает, запись отвечает 403 read_only.

Общие правила

Формат ответа

Ответ всегда JSON в конверте. Успех — ok: true и данные в data; у списков и записей броней — ещё meta:

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2opr9e6k0009m1n3p5q7r9s"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 50,
    "total": 1,
    "pages": 1
  }
}

Ошибка — ok: false и объект error:

json
{
  "ok": false,
  "error": {
    "code": "validation_error",
    "message": "Проверьте параметры запроса",
    "details": [
      {
        "path": "amount",
        "message": "Сумма > 0"
      }
    ]
  }
}
  • code — машинный код ошибки: набор фиксирован, ветвитесь по нему.
  • message — текст для человека по-русски; он может меняться, не сравнивайте его в коде.
  • details — подробности, если есть: поля с ошибками у validation_error, нужный скоуп у forbidden, текущая версия брони у conflict.
  • HTTP-код ответа соответствует коду ошибки (таблица ниже). Успешная запись нового объекта — 201, остальное — 200.

Коды ошибок

HTTPКодКогдаЧто делать
401unauthorizedТокен не передан, неверен, отозван или истёк; выпустивший токен больше не владелец и не администратор организации.Повтор не поможет: проверьте заголовок Authorization или выпустите новый токен.
403forbiddenУ токена нет нужного скоупа (details.required, details.granted) или в тарифе организации нет нужной функции (details.feature, details.requiredPlan — тариф, где она есть). При записи операции — исчерпан лимит тарифа (details.limit, details.max, details.used) или у выпустившего токен нет права или доступа к счёту и направлению.Выпустите токен с нужным скоупом, смените тариф или проверьте права выпустившего токен.
403read_onlyЗапись в организации, которая в режиме чтения: подписка не оплачена или на паузе. Чтение при этом работает.Запись снова станет доступна после оплаты; повторите позже.
404not_foundОбъекта нет — или он принадлежит другой организации: чужое и несуществующее отвечают одинаково.Проверьте идентификатор; у оплаты брони — что сама бронь уже передана.
409conflictПришла старая версия брони (details.currentVersion), та же версия с другими данными или правка оплаты, которая уже в учёте; Idempotency-Key операции, которую создали и потом удалили.Перечитайте объект (GET) и не отправляйте устаревшее; при изменении брони поднимайте version.
422validation_errorНеверные параметры или тело запроса; тело — не JSON. В details — список { path, message } по полям.Исправьте запрос по details; повтор без исправления даст ту же ошибку.
429rate_limitedПревышен лимит частоты запросов токена или организации.Подождите столько секунд, сколько в заголовке Retry-After, и повторите.
500internal_errorСбой на стороне Aynes.Повторите позже с паузой. Записи броней и оплат идемпотентны — их повтор безопасен.

Лимиты частоты

Не больше 120 запросов в минуту на токен и 600 в минуту на организацию — на все её токены вместе. Окно — календарная минута: счётчик обнуляется в начале каждой минуты.

ЗаголовокЧто значит
X-RateLimit-LimitЛимит токена в минуту
X-RateLimit-RemainingСколько запросов осталось в этой минуте — меньшее из остатков токена и организации
Retry-AfterТолько в ответе 429: через сколько секунд повторить

Заголовки X-RateLimit-* приходят в успешных ответах и в ответе 429. Превышение:

http
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Retry-After: 17

{"ok":false,"error":{"code":"rate_limited","message":"Слишком много запросов, попробуйте позже"}}

Для выгрузки истории идите страницами по 200 записей и делайте паузу по Retry-After; для новых событий подпишитесь на вебхуки вместо частого опроса.

Постраничная выдача

Списки GET /operations, GET /payment-requests и GET /bookings отдаются страницами: page — номер с 1, pageSize — от 1 до 200, по умолчанию 50. В meta — номер и размер страницы, всего записей под фильтры и всего страниц. Справочники отдаются целиком; контрагентов — не больше 500 первых по алфавиту, остальных находит параметр search.

js
// Node.js 18+: все операции за октябрь
const base = "https://aynes.pro/api/v1";
const headers = { Authorization: `Bearer ${process.env.AYNES_TOKEN}` };
const operations = [];
for (let page = 1; ; page++) {
  const url = `${base}/operations?from=2026-10-01&to=2026-10-31&pageSize=200&page=${page}`;
  const res = await fetch(url, { headers });
  const body = await res.json();
  if (!body.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
  operations.push(...body.data);
  if (page >= body.meta.pages) break;
}

Пока вы идёте по страницам, в организации могут появиться новые операции, и записи сдвинутся. Для сверки сужайте период или берите новые операции из вебхука operation.created.

Идемпотентность и версии

  • PUT брони и оплаты брони идемпотентны: ключ — путь (source, externalId, paymentId). Повтор того же тела — 200 и meta.changed = false, ничего не задваивается; новая запись — 201 и meta.created = true. После таймаута или ответа 5xx такой запрос можно смело повторить.
  • Бронь несёт version — число, которое растёт при каждом изменении брони в вашей системе. Версия старше той, что в Aynes, или та же версия с другими данными — 409 conflict с details.currentVersion. Без version сравнивается updatedAt (время изменения у вас); без обоих побеждает последний запрос.
  • Оплату, которая уже проведена в кассу или склеена с выпиской, поменять нельзя (сумма, валюта, способ, вид, комиссия, дата) — 409 conflict; provider и payoutRef меняются всегда.
  • POST /operations идемпотентен по заголовку Idempotency-Key: повтор с тем же ключом — 200, meta.repeated = true и первая операция, вторая не заводится. Ключ живёт у токена бессрочно — берите свой на каждую операцию, например номер документа. Без заголовка повтор создаст вторую операцию.
  • POST /payment-requests не идемпотентен: повтор создаст вторую заявку. После таймаута проверьте GET /payment-requests?createdFrom=….

Профиль

get/me

Токен и организация

Скоуп:
любой действующий токен
Функция тарифа:
«API»

Первый запрос интеграции: проверяет токен и возвращает его скоупы и организацию — базовую валюту, страну и часовой пояс. Нужен любой действующий токен; ключу броней на тарифе без функции «API» метод отвечает 403.

Пример запроса

curl
curl "https://aynes.pro/api/v1/me" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "token": {
      "id": "cm2tok8x1a0001d4n7q2k5v9b",
      "name": "Выгрузка в 1С",
      "scopes": [
        "operations:read",
        "references:read",
        "reports:read"
      ]
    },
    "tenant": {
      "id": "cm2tnt3p0b0000a1b2c3d4e5f",
      "name": "Ала-Тоо Сервис",
      "baseCurrency": "KGS",
      "country": "KG",
      "timezone": "Asia/Bishkek"
    }
  }
}

Поля ответа

ПолеТипОписание
tokenobjectТокен запроса. Поля: id, name, scopes.
tenantobjectОрганизация токена. Поля: id, name, baseCurrency, country, timezone.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Операции

Операции — факт и план движения денег: приходы, расходы и переводы между своими счетами. Через API их читают (operations:read) и заводят (operations:write) — так же, как форма операции в Aynes.

get/operations

Список операций

Скоуп:
operations:read
Функция тарифа:
«API»

Тот же список, что на экране «Операции»: те же фильтры и сортировка. Удалённые операции не отдаются. Сумма — в валюте счёта (amount, currency), эквивалент — в базовой валюте организации по курсу на дату операции (baseAmount, exchangeRate).

  • type: INCOME — приход, OUTCOME — расход, TRANSFER — перевод между своими счетами (counterAccount — счёт получателя).
  • Обмен валюты — TRANSFER между счетами разных валют: amount списано в валюте счёта, counterAmount зачислено в валюте получателя (counterCurrency), counterBaseAmount − baseAmount — курсовая разница в базовой валюте. У обычного перевода эти поля null.
  • status: ACTUAL — факт, PLANNED — план (ожидаемый платёж).
  • source — откуда операция: MANUAL — введена вручную, BANK_STATEMENT — из выписки, MINIAPP — из мини-приложения Telegram и другие.
  • Новые операции удобнее получать вебхуком operation.created, а не опросом.

Параметры запроса

ПараметрТипОбязательныйОписание
fromstring (date)нетДата операции с этого дня. День ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Формат ГГГГ-ММ-ДД.
tostring (date)нетДата операции по этот день включительно. День ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Формат ГГГГ-ММ-ДД.
dateBasisstringнетПо какой дате резать from и to: дата операции или дата начисления (без неё — дата операции). Значения: operation, accrual. По умолчанию operation.
statusstringнетПлан или факт. Значения: PLANNED, ACTUAL.
typestringнетПриход, расход или перевод. Значения: INCOME, OUTCOME, TRANSFER.
unitIdstringнетНаправление.
legalEntityIdstringнетЮрлицо.
bankAccountIdstringнетСчёт или касса.
contractorIdstringнетКонтрагент.
categoryIdstringнетКатегория.
projectIdstringнетПроект.
directionIdstringнетУчасток работ проекта.
tagstringнетМетка операции.
noCategorystringнетТолько операции без категории. Значения: 1, true.
noUnitstringнетТолько приходы и расходы без направления. Значения: 1, true.
searchstringнетСлова назначения и имени контрагента, сумма, номер документа, ИНН.
sortstringнетСортировка; сумма — по эквиваленту в базовой валюте. Значения: date, amount, category, contractor, account, status. По умолчанию date.
dirstringнетНаправление сортировки. Значения: asc, desc. По умолчанию desc.
pageintegerнетНомер страницы. По умолчанию 1.
pageSizeintegerнетЗаписей на странице. По умолчанию 50. Не больше 200.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/operations" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -d from=2026-10-01 -d to=2026-10-31 \
  -d type=OUTCOME -d status=ACTUAL \
  -d page=1 -d pageSize=50

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2opr9e6k0009m1n3p5q7r9s",
      "type": "OUTCOME",
      "status": "ACTUAL",
      "amount": 4500000,
      "currency": "KGS",
      "exchangeRate": "1",
      "baseAmount": 4500000,
      "counterAccount": null,
      "counterAmount": null,
      "counterCurrency": null,
      "counterBaseAmount": null,
      "operationDate": "2026-10-03T00:00:00.000Z",
      "accrualDate": "2026-10-01T00:00:00.000Z",
      "description": "Аренда офиса за октябрь 2026",
      "source": "BANK_STATEMENT",
      "unit": {
        "id": "cm2unt5r2c0003h6j8k1m3n5p",
        "name": "Бишкек, офис"
      },
      "bankAccount": {
        "id": "cm2acc9u6e0004y5z7a9b2c4d",
        "name": "MBANK, расчётный KGS"
      },
      "contractor": {
        "id": "cm2cnt3y0g0006n7p9q2r4s6t",
        "name": "ОсОО «Бизнес Центр Орион»"
      },
      "category": {
        "id": "cm2cat5a2h0007u8w1x3y5z7a",
        "name": "Аренда"
      },
      "project": null,
      "tags": [
        "офис"
      ],
      "createdAt": "2026-10-03T08:12:44.512Z"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 50,
    "total": 1,
    "pages": 1
  }
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
typestringЗначения: INCOME, OUTCOME, TRANSFER.
statusstringЗначения: PLANNED, ACTUAL.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
currencystring
exchangeRatestringКурс к базовой валюте организации, Decimal строкой.
baseAmountintegerЭквивалент в базовой валюте организации.
counterAccountobjectСчёт получателя перевода. Может быть null. Поля: id, name.
counterAmountintegerОбмен валюты: сколько зачислено на счёт получателя в его валюте; null — не обмен. Может быть null.
counterCurrencystringОбмен валюты: валюта зачисления. Может быть null.
counterBaseAmountintegerОбмен валюты: эквивалент зачисленного в базовой валюте; минус baseAmount — курсовая разница. Может быть null.
operationDatestring (date-time)ISO 8601.
accrualDatestring (date-time)ISO 8601. Может быть null.
descriptionstringМожет быть null.
sourcestring
unitobjectМожет быть null. Поля: id, name.
bankAccountobjectМожет быть null. Поля: id, name.
contractorobjectМожет быть null. Поля: id, name.
categoryobjectМожет быть null. Поля: id, name.
projectobjectМожет быть null. Поля: id, name.
tagsarray<string>Метки операции.
createdAtstring (date-time)ISO 8601.
meta.pageintegerНомер страницы, с 1.
meta.pageSizeintegerЗаписей на странице.
meta.totalintegerВсего записей под фильтры.
meta.pagesintegerВсего страниц.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

post/operations

Создать операцию

Скоуп:
operations:write
Функция тарифа:
«API»

Операция заводится от имени того, кто выпустил токен, и с его правами: нужно право «Создавать операции», счёт и направление — из доступных ему. Проверки те же, что у формы операции в Aynes: справочники этой организации, направление у прихода и расхода, у перевода — счёт получателя той же валюты, категория того же типа, счёт не закрыт и не «не учитывать».

  • Валюта операции — валюта счёта. Эквивалент в базовой валюте (baseAmount) считается по курсу справочника на дату операции и потом не пересчитывается. Курса на дату нет — 422 с details[].path = "exchangeRate": передайте курс в exchangeRate.
  • Лимит операций в месяц по тарифу — 403 forbidden с details.limit = "operations". Организация в режиме чтения — 403 read_only.
  • Чужой или несуществующий счёт, направление, категория, проект или контрагент — 404 not_found.
  • Подписчикам уходит вебхук operation.created; в журнале Aynes — «из API» с токеном.

Передавайте заголовок Idempotency-Key — свой у каждой операции (например, номер документа в вашей системе). Повтор с тем же ключом вернёт 200 и первую операцию (meta.repeated = true), вторая не заведётся. Без ключа повтор после таймаута создаст вторую операцию.

Заголовки запроса

ПараметрТипОбязательныйОписание
Idempotency-KeystringнетКлюч запроса: до 200 печатных символов ASCII без пробелов, свой у каждой операции. Повтор с тем же ключом — 200 и первая операция (meta.repeated = true), вторая не заводится. До 200 символов.

Тело запроса

ПолеТипОбязательныйОписание
typestringдаПриход, расход или перевод между своими счетами. Значения: INCOME, OUTCOME, TRANSFER.
statusstringнетФакт или план (ожидаемый платёж). Значения: ACTUAL, PLANNED. По умолчанию ACTUAL.
amountintegerдаСумма в минимальных единицах валюты счёта, больше нуля.
operationDatestring (date)даДата операции. День ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Формат ГГГГ-ММ-ДД.
accrualDatestring (date)нетДата начисления для «Прибылей и убытков» по начислению; пусто — дата операции. Формат ГГГГ-ММ-ДД. Может быть null.
bankAccountIdstringдаСчёт или касса — из GET /references/bank-accounts; валюта операции — валюта счёта.
counterAccountIdstringнетСчёт получателя — обязателен у перевода, той же валюты. Может быть null.
unitIdstringнетНаправление — обязательно у прихода и расхода. Может быть null.
categoryIdstringнетКатегория того же типа, что операция. Может быть null.
projectIdstringнетПроект. Может быть null.
directionIdstringнетУчасток работ проекта. Может быть null.
contractorIdstringнетКонтрагент. Может быть null.
descriptionstringнетНазначение. До 500 символов. Может быть null.
tagsarray<string>нетМетки операции.
exchangeRatestringнетКурс к базовой валюте строкой, например "89.5"; пусто — курс справочника на дату. Курса на дату нет — 422 «укажите курс вручную». Может быть null.

Пример запроса

curl
curl -X POST "https://aynes.pro/api/v1/operations" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -H "Idempotency-Key: erp-2026-10-03-118" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "OUTCOME",
    "status": "ACTUAL",
    "amount": 4500000,
    "operationDate": "2026-10-03",
    "accrualDate": "2026-10-01",
    "bankAccountId": "cm2acc9u6e0004y5z7a9b2c4d",
    "unitId": "cm2unt5r2c0003h6j8k1m3n5p",
    "categoryId": "cm2cat5a2h0007u8w1x3y5z7a",
    "contractorId": "cm2cnt3y0g0006n7p9q2r4s6t",
    "description": "Аренда офиса за октябрь 2026",
    "tags": [
      "офис"
    ]
  }'

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2opr9e6k0009m1n3p5q7r9s",
    "type": "OUTCOME",
    "status": "ACTUAL",
    "amount": 4500000,
    "currency": "KGS",
    "exchangeRate": "1",
    "baseAmount": 4500000,
    "counterAccount": null,
    "counterAmount": null,
    "counterCurrency": null,
    "counterBaseAmount": null,
    "operationDate": "2026-10-03T00:00:00.000Z",
    "accrualDate": "2026-10-01T00:00:00.000Z",
    "description": "Аренда офиса за октябрь 2026",
    "source": "MANUAL",
    "unit": {
      "id": "cm2unt5r2c0003h6j8k1m3n5p",
      "name": "Бишкек, офис"
    },
    "bankAccount": {
      "id": "cm2acc9u6e0004y5z7a9b2c4d",
      "name": "MBANK, расчётный KGS"
    },
    "contractor": {
      "id": "cm2cnt3y0g0006n7p9q2r4s6t",
      "name": "ОсОО «Бизнес Центр Орион»"
    },
    "category": {
      "id": "cm2cat5a2h0007u8w1x3y5z7a",
      "name": "Аренда"
    },
    "project": null,
    "tags": [
      "офис"
    ],
    "createdAt": "2026-10-03T08:12:44.512Z"
  },
  "meta": {
    "repeated": false
  }
}

Поля ответа

ПолеТипОписание
idstring
typestringЗначения: INCOME, OUTCOME, TRANSFER.
statusstringЗначения: PLANNED, ACTUAL.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
currencystring
exchangeRatestringКурс к базовой валюте организации, Decimal строкой.
baseAmountintegerЭквивалент в базовой валюте организации.
counterAccountobjectСчёт получателя перевода. Может быть null. Поля: id, name.
counterAmountintegerОбмен валюты: сколько зачислено на счёт получателя в его валюте; null — не обмен. Может быть null.
counterCurrencystringОбмен валюты: валюта зачисления. Может быть null.
counterBaseAmountintegerОбмен валюты: эквивалент зачисленного в базовой валюте; минус baseAmount — курсовая разница. Может быть null.
operationDatestring (date-time)ISO 8601.
accrualDatestring (date-time)ISO 8601. Может быть null.
descriptionstringМожет быть null.
sourcestring
unitobjectМожет быть null. Поля: id, name.
bankAccountobjectМожет быть null. Поля: id, name.
contractorobjectМожет быть null. Поля: id, name.
categoryobjectМожет быть null. Поля: id, name.
projectobjectМожет быть null. Поля: id, name.
tagsarray<string>Метки операции.
createdAtstring (date-time)ISO 8601.
meta.repeatedbooleantrue — повтор по Idempotency-Key, операция создана раньше.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbidden, read_onlyУ токена нет нужного скоупа, тариф без нужной функции или (для записи) организация в режиме чтения
404not_foundСчёт, направление, категория, проект или контрагент не найдены в организации
409conflictОперация с этим Idempotency-Key создана и удалена — нужен новый ключ
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Заявки на оплату

Заявка — просьба оплатить расход: её согласуют по маршруту организации, потом склеивают с операцией оплаты. Статусы: PENDING — на согласовании, APPROVED — согласована, REJECTED — отклонена, PAID — оплачена, CANCELLED — отменена. Черновики (DRAFT) — личная заготовка автора, во внешний API они не попадают.

get/payment-requests

Список заявок на оплату

Скоуп:
payment-requests:read
Функция тарифа:
«API»

Заявки всей организации без черновиков, свежие сверху, с ходом согласования и вложениями. approval — маршрут: сколько шагов, чей шаг сейчас (isCurrent) и кто уже решил; null — заявка согласуется в один шаг. Вложения отдаются именем, размером и типом, без самих файлов.

Параметры запроса

ПараметрТипОбязательныйОписание
statusstringнетСтатус заявки; ALL — все, кроме черновиков. Значения: PENDING, APPROVED, REJECTED, PAID, CANCELLED, ALL. По умолчанию ALL.
unitIdstringнетНаправление.
createdFromstring (date)нетСозданные с этой даты включительно (UTC). Формат ГГГГ-ММ-ДД.
createdTostring (date)нетСозданные по эту дату включительно (UTC). Формат ГГГГ-ММ-ДД.
pageintegerнетНомер страницы. По умолчанию 1.
pageSizeintegerнетЗаписей на странице. По умолчанию 50. Не больше 200.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/payment-requests" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -d status=PENDING -d createdFrom=2026-10-01

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2req1g8m0010t2v4w6x8y1z",
      "status": "PENDING",
      "amount": 1250000,
      "currency": "USD",
      "exchangeRate": "87.45",
      "baseAmount": 109312500,
      "description": "Предоплата за оборудование по счёту № 118",
      "dueDate": "2026-10-15T00:00:00.000Z",
      "expectedPaidAt": null,
      "paidAt": null,
      "decisionAt": null,
      "rejectionReason": null,
      "requester": {
        "id": "cm2usr3h9n0011a3b5c7d9e1f",
        "name": "Айгуль Сыдыкова"
      },
      "approver": null,
      "unit": {
        "id": "cm2unt5r2c0003h6j8k1m3n5p",
        "name": "Бишкек, офис"
      },
      "category": {
        "id": "cm2cat5a2h0007u8w1x3y5z7a",
        "name": "Оборудование"
      },
      "project": {
        "id": "cm2prj7c4j0008b9d2f4g6h8k",
        "name": "Открытие филиала в Оше"
      },
      "contractor": {
        "id": "cm2cnt4z1h0016p8q1r3s5t7u",
        "name": "ОсОО «ТехноСнаб»"
      },
      "bankAccount": {
        "id": "cm2acc9u6e0004y5z7a9b2c4d",
        "name": "MBANK, расчётный USD"
      },
      "matchedOperationId": null,
      "hasMatchConflict": false,
      "approval": {
        "routeId": "cm2rte5k2q0014n5p7q9r2s4t",
        "routeName": "Крупные расходы",
        "totalSteps": 2,
        "currentStep": 2,
        "steps": [
          {
            "order": 1,
            "approverRole": "UNIT_HEAD",
            "approverName": null,
            "decision": "APPROVED",
            "decidedAt": "2026-10-06T10:02:11.000Z",
            "decidedByName": "Бакыт Осмонов",
            "comment": null,
            "isCurrent": false
          },
          {
            "order": 2,
            "approverRole": null,
            "approverName": "Нурлан Абдыкадыров",
            "decision": null,
            "decidedAt": null,
            "decidedByName": null,
            "comment": null,
            "isCurrent": true
          }
        ]
      },
      "attachments": [
        {
          "id": "cm2att6m3r0015u6v8w1x3y5z",
          "filename": "schet-118.pdf",
          "size": 184320,
          "mime": "application/pdf"
        }
      ],
      "createdAt": "2026-10-06T09:41:27.330Z",
      "updatedAt": "2026-10-06T10:02:11.000Z"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 50,
    "total": 1,
    "pages": 1
  }
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
statusstringЗначения: PENDING, APPROVED, REJECTED, PAID, CANCELLED.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
currencystring
exchangeRatestringКурс к базовой валюте организации, Decimal строкой.
baseAmountintegerЭквивалент в базовой валюте организации.
descriptionstring
dueDatestring (date-time)ISO 8601. Может быть null.
expectedPaidAtstring (date-time)ISO 8601. Может быть null.
paidAtstring (date-time)ISO 8601. Может быть null.
decisionAtstring (date-time)ISO 8601. Может быть null.
rejectionReasonstringМожет быть null.
requesterobjectМожет быть null. Поля: id, name.
approverobjectМожет быть null. Поля: id, name.
unitobjectМожет быть null. Поля: id, name.
categoryobjectМожет быть null. Поля: id, name.
projectobjectМожет быть null. Поля: id, name.
contractorobjectМожет быть null. Поля: id, name.
bankAccountobjectМожет быть null. Поля: id, name.
matchedOperationIdstringОперация, которой заявка оплачена. Может быть null.
hasMatchConflictboolean
approvalobjectХод согласования по маршруту; null — заявка согласуется в один шаг. Может быть null. Поля: routeId, routeName, totalSteps, currentStep, steps.
attachmentsarray<object>Поля элемента: id, filename, size, mime.
createdAtstring (date-time)ISO 8601.
updatedAtstring (date-time)ISO 8601.
meta.pageintegerНомер страницы, с 1.
meta.pageSizeintegerЗаписей на странице.
meta.totalintegerВсего записей под фильтры.
meta.pagesintegerВсего страниц.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

post/payment-requests

Создать заявку на оплату

Скоуп:
payment-requests:write
Функция тарифа:
«API»

Заявка создаётся от имени того, кто выпустил токен, и сразу уходит на согласование по маршруту организации — согласующие увидят её в Aynes и в Telegram. Справочники проверяются: направление, категория, проект, контрагент и счёт должны быть этой организации, иначе 422.

Метод не идемпотентен: повтор создаст вторую заявку. После таймаута сначала проверьте GET /payment-requests?createdFrom=…, не появилась ли заявка.

Тело запроса

ПолеТипОбязательныйОписание
amountintegerдаСумма в минимальных единицах валюты заявки, больше нуля.
currencystringнетКод ISO 4217; пусто — валюта счёта, а без счёта — базовая. Может быть null.
exchangeRatestringнетКурс к базовой валюте строкой, например "89.5"; пусто — курс справочника на срок заявки. Может быть null.
descriptionstringдаНазначение платежа. До 500 символов.
unitIdstringдаНаправление — из GET /references/units.
categoryIdstringнетКатегория расхода. Может быть null.
projectIdstringнетПроект. Может быть null.
directionIdstringнетУчасток работ проекта; обязателен, если у проекта участки есть. Может быть null.
contractorIdstringнетПолучатель платежа. Может быть null.
bankAccountIdstringнетСчёт, с которого платить. Может быть null.
dueDatestring (date)нетСрок оплаты: не раньше года назад и не позже трёх лет вперёд. Формат ГГГГ-ММ-ДД. Может быть null.
payrollEmployeeIdstringнетСотрудник для выплаты зарплаты — только у категории с признаком «Зарплата». Может быть null.
payrollMonthstringнетМесяц выплаты зарплаты, «ГГГГ-ММ». Может быть null.

Пример запроса

curl
curl -X POST "https://aynes.pro/api/v1/payment-requests" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 1250000,
    "currency": "USD",
    "description": "Предоплата за оборудование по счёту № 118",
    "unitId": "cm2unt5r2c0003h6j8k1m3n5p",
    "categoryId": "cm2cat5a2h0007u8w1x3y5z7a",
    "projectId": "cm2prj7c4j0008b9d2f4g6h8k",
    "contractorId": "cm2cnt4z1h0016p8q1r3s5t7u",
    "bankAccountId": "cm2acc9u6e0004y5z7a9b2c4d",
    "dueDate": "2026-10-15"
  }'

Пример ответа · 201

json
{
  "ok": true,
  "data": {
    "id": "cm2req1g8m0010t2v4w6x8y1z",
    "status": "PENDING",
    "amount": 1250000,
    "currency": "USD",
    "baseAmount": 109312500,
    "dueDate": "2026-10-15T00:00:00.000Z",
    "description": "Предоплата за оборудование по счёту № 118",
    "createdAt": "2026-10-08T07:30:12.004Z"
  }
}

Поля ответа

ПолеТипОписание
idstring
statusstringЗначения: PENDING.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
currencystring
baseAmountintegerЭквивалент в базовой валюте организации.
dueDatestring (date-time)ISO 8601. Может быть null.
descriptionstring
createdAtstring (date-time)ISO 8601.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbidden, read_onlyУ токена нет нужного скоупа, тариф без нужной функции или (для записи) организация в режиме чтения
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/payment-requests/{id}

Одна заявка на оплату

Скоуп:
payment-requests:read
Функция тарифа:
«API»

Одна заявка в той же форме, что в списке. Её перечитывают после вебхуков payment_request.status_changed и payment_request.updated: в теле события — только идентификатор и что изменилось. Чужая заявка и черновик отвечают 404.

Параметры пути

ПараметрТипОбязательныйОписание
idstringдаИдентификатор заявки.

Пример запроса

curl
curl "https://aynes.pro/api/v1/payment-requests/cm2req1g8m0010t2v4w6x8y1z" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2req1g8m0010t2v4w6x8y1z",
    "status": "PENDING",
    "amount": 1250000,
    "currency": "USD",
    "exchangeRate": "87.45",
    "baseAmount": 109312500,
    "description": "Предоплата за оборудование по счёту № 118",
    "dueDate": "2026-10-15T00:00:00.000Z",
    "expectedPaidAt": null,
    "paidAt": null,
    "decisionAt": null,
    "rejectionReason": null,
    "requester": {
      "id": "cm2usr3h9n0011a3b5c7d9e1f",
      "name": "Айгуль Сыдыкова"
    },
    "approver": null,
    "unit": {
      "id": "cm2unt5r2c0003h6j8k1m3n5p",
      "name": "Бишкек, офис"
    },
    "category": {
      "id": "cm2cat5a2h0007u8w1x3y5z7a",
      "name": "Оборудование"
    },
    "project": {
      "id": "cm2prj7c4j0008b9d2f4g6h8k",
      "name": "Открытие филиала в Оше"
    },
    "contractor": {
      "id": "cm2cnt4z1h0016p8q1r3s5t7u",
      "name": "ОсОО «ТехноСнаб»"
    },
    "bankAccount": {
      "id": "cm2acc9u6e0004y5z7a9b2c4d",
      "name": "MBANK, расчётный USD"
    },
    "matchedOperationId": null,
    "hasMatchConflict": false,
    "approval": {
      "routeId": "cm2rte5k2q0014n5p7q9r2s4t",
      "routeName": "Крупные расходы",
      "totalSteps": 2,
      "currentStep": 2,
      "steps": [
        {
          "order": 1,
          "approverRole": "UNIT_HEAD",
          "approverName": null,
          "decision": "APPROVED",
          "decidedAt": "2026-10-06T10:02:11.000Z",
          "decidedByName": "Бакыт Осмонов",
          "comment": null,
          "isCurrent": false
        },
        {
          "order": 2,
          "approverRole": null,
          "approverName": "Нурлан Абдыкадыров",
          "decision": null,
          "decidedAt": null,
          "decidedByName": null,
          "comment": null,
          "isCurrent": true
        }
      ]
    },
    "attachments": [
      {
        "id": "cm2att6m3r0015u6v8w1x3y5z",
        "filename": "schet-118.pdf",
        "size": 184320,
        "mime": "application/pdf"
      }
    ],
    "createdAt": "2026-10-06T09:41:27.330Z",
    "updatedAt": "2026-10-06T10:02:11.000Z"
  }
}

Поля ответа

ПолеТипОписание
idstring
statusstringЗначения: PENDING, APPROVED, REJECTED, PAID, CANCELLED.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
currencystring
exchangeRatestringКурс к базовой валюте организации, Decimal строкой.
baseAmountintegerЭквивалент в базовой валюте организации.
descriptionstring
dueDatestring (date-time)ISO 8601. Может быть null.
expectedPaidAtstring (date-time)ISO 8601. Может быть null.
paidAtstring (date-time)ISO 8601. Может быть null.
decisionAtstring (date-time)ISO 8601. Может быть null.
rejectionReasonstringМожет быть null.
requesterobjectМожет быть null. Поля: id, name.
approverobjectМожет быть null. Поля: id, name.
unitobjectМожет быть null. Поля: id, name.
categoryobjectМожет быть null. Поля: id, name.
projectobjectМожет быть null. Поля: id, name.
contractorobjectМожет быть null. Поля: id, name.
bankAccountobjectМожет быть null. Поля: id, name.
matchedOperationIdstringОперация, которой заявка оплачена. Может быть null.
hasMatchConflictboolean
approvalobjectХод согласования по маршруту; null — заявка согласуется в один шаг. Может быть null. Поля: routeId, routeName, totalSteps, currentStep, steps.
attachmentsarray<object>Поля элемента: id, filename, size, mime.
createdAtstring (date-time)ISO 8601.
updatedAtstring (date-time)ISO 8601.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
404not_foundЗаявка не найдена
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Справочники

Справочники отдаются целиком, без страниц: идентификаторы из них нужны фильтрам операций и телу заявки. Скоуп у всех — references:read.

get/references/units

Направления

Скоуп:
references:read
Функция тарифа:
«API»

Направление — направление учёта организации (филиал, магазин, лагерь); у каждого — юрлицо. Обязателен в заявке на оплату (unitId). Отдаются только действующие направления.

Пример запроса

curl
curl "https://aynes.pro/api/v1/references/units" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2unt5r2c0003h6j8k1m3n5p",
      "name": "Бишкек, офис",
      "legalEntity": {
        "id": "cm2lgl7s4d0002q4r6t8v1w3x",
        "name": "ОсОО «Ала-Тоо Сервис»"
      }
    }
  ]
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
namestring
legalEntityobjectМожет быть null. Поля: id, name.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/references/categories

Категории

Скоуп:
references:read
Функция тарифа:
«API»

Действующие категории прихода и расхода с деревом (parentId) и разделом ПиУ. Служебные категории (isInternal: переводы в копилки, депозиты) не входят в обороты ДДС.

Пример запроса

curl
curl "https://aynes.pro/api/v1/references/categories" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2cat6b3j0017w9x2y4z6a8b",
      "name": "Выручка от услуг",
      "type": "INCOME",
      "parentId": null,
      "isInternal": false,
      "pnlSection": {
        "id": "cm2sec8d5k0018x1y3z5a7b9c",
        "name": "Выручка"
      }
    },
    {
      "id": "cm2cat5a2h0007u8w1x3y5z7a",
      "name": "Аренда",
      "type": "OUTCOME",
      "parentId": null,
      "isInternal": false,
      "pnlSection": null
    }
  ]
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
namestring
typestringЗначения: INCOME, OUTCOME.
parentIdstringРодительская категория. Может быть null.
isInternalbooleanСлужебная категория: переводы в копилки, депозиты.
pnlSectionobjectРаздел ПиУ. Может быть null. Поля: id, name.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/references/contractors

Контрагенты

Скоуп:
references:read
Функция тарифа:
«API»

Не больше 500 первых по алфавиту — ищите параметром search.

Параметры запроса

ПараметрТипОбязательныйОписание
searchstringнетЧасть названия, без учёта регистра.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/references/contractors" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  --data-urlencode "search=Орион"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2cnt3y0g0006n7p9q2r4s6t",
      "name": "ОсОО «Бизнес Центр Орион»",
      "inn": "01408201610045",
      "kind": "LEGAL_ENTITY"
    }
  ]
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
namestring
innstringМожет быть null.
kindstringЮрлицо, ИП или физлицо. Значения: LEGAL_ENTITY, INDIVIDUAL_ENTREPRENEUR, INDIVIDUAL.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/references/bank-accounts

Счета и кассы

Скоуп:
references:read
Функция тарифа:
«API»

Действующие счета и кассы.

Пример запроса

curl
curl "https://aynes.pro/api/v1/references/bank-accounts" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2acc9u6e0004y5z7a9b2c4d",
      "name": "MBANK, расчётный KGS",
      "currency": "KGS",
      "kind": "BANK",
      "legalEntity": {
        "id": "cm2lgl7s4d0002q4r6t8v1w3x",
        "name": "ОсОО «Ала-Тоо Сервис»"
      }
    },
    {
      "id": "cm2cas1w8f0005e6g8h1j3k5m",
      "name": "Касса офиса",
      "currency": "KGS",
      "kind": "CASH",
      "legalEntity": {
        "id": "cm2lgl7s4d0002q4r6t8v1w3x",
        "name": "ОсОО «Ала-Тоо Сервис»"
      }
    }
  ]
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
namestring
currencystring
kindstringБанковский счёт, касса или депозит. Значения: BANK, CASH, DEPOSIT.
legalEntityobjectМожет быть null. Поля: id, name.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/references/projects

Проекты

Скоуп:
references:read
Функция тарифа:
«API»

Проекты не в архиве.

Пример запроса

curl
curl "https://aynes.pro/api/v1/references/projects" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2prj7c4j0008b9d2f4g6h8k",
      "name": "Открытие филиала в Оше",
      "status": "ACTIVE",
      "startDate": "2026-09-01T00:00:00.000Z",
      "endDate": "2026-12-20T00:00:00.000Z",
      "unit": {
        "id": "cm2unt5r2c0003h6j8k1m3n5p",
        "name": "Бишкек, офис"
      },
      "client": null
    }
  ]
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
namestring
statusstringЗначения: PLANNED, ACTIVE, ON_HOLD, FINISHED.
startDatestring (date-time)ISO 8601. Может быть null.
endDatestring (date-time)ISO 8601. Может быть null.
unitobjectМожет быть null. Поля: id, name.
clientobjectКлиент проекта — контрагент. Может быть null. Поля: id, name.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Отчёты

Отчёты считаются тем же сервисом, что экраны Aynes, — числа совпадают до тыйына. Суммы — в минимальных единицах базовой валюты организации; дни и месяцы — в часовом поясе организации (tenant.timezone в GET /me).

get/reports/cashflow

ДДС за период

Скоуп:
reports:read
Функция тарифа:
«API»

Движение денег за период кассовым методом: остатки на начало и конец, обороты по категориям и по направлениям, юрлицам или счетам. Переводы между своими счетами и служебные категории в обороты не входят — их сумма в excludedNet, так что всегда opening + totalIncome − totalExpense + excludedNet = closing.

Параметры запроса

ПараметрТипОбязательныйОписание
dateFromstring (date)даДень ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Начало не позже конца, период не длиннее 10 лет. Формат ГГГГ-ММ-ДД.
dateTostring (date)даДень ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Начало не позже конца, период не длиннее 10 лет. Формат ГГГГ-ММ-ДД.
groupBystringнетРазрез byGroup. Значения: unit, legalEntity, bankAccount. По умолчанию unit.
unitIdstringнетТолько это направление.
legalEntityIdstringнетТолько это юрлицо.
bankAccountIdstringнетТолько этот счёт.
includePlannedstringнетУчесть и плановые операции; без него — только факт. Значения: 1, true.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/reports/cashflow" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -d dateFrom=2026-09-01 -d dateTo=2026-09-30 -d groupBy=unit

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "dateFrom": "2026-09-01T00:00:00.000Z",
    "dateTo": "2026-09-30T00:00:00.000Z",
    "groupBy": "unit",
    "opening": 152000000,
    "closing": 171850000,
    "totalIncome": 98400000,
    "totalExpense": 76550000,
    "excludedNet": -2000000,
    "byCategory": [
      {
        "categoryId": "cm2cat6b3j0017w9x2y4z6a8b",
        "categoryName": "Выручка от услуг",
        "income": 98400000,
        "expense": 0
      },
      {
        "categoryId": "cm2cat5a2h0007u8w1x3y5z7a",
        "categoryName": "Аренда",
        "income": 0,
        "expense": 4500000
      }
    ],
    "byGroup": [
      {
        "id": "cm2unt5r2c0003h6j8k1m3n5p",
        "name": "Бишкек, офис",
        "income": 98400000,
        "expense": 76550000,
        "opening": null,
        "closing": null
      }
    ]
  }
}

Поля ответа

ПолеТипОписание
dateFromstring (date-time)Первый день периода — полночь UTC. ISO 8601.
dateTostring (date-time)Последний день периода — полночь UTC. ISO 8601.
groupBystringЗначения: unit, legalEntity, bankAccount.
openingintegerОстаток счетов на начало периода.
closingintegerОстаток счетов на конец периода.
totalIncomeintegerПоступления за период (без переводов между своими счетами).
totalExpenseintegerВыплаты за период.
excludedNetintegerДвижения вне оборотов отчёта (переводы, служебные категории): opening + totalIncome − totalExpense + excludedNet = closing.
byCategoryarray<object>Обороты по категориям. Поля элемента: categoryId, categoryName, income, expense.
byGrouparray<object>Обороты и остатки по направлениям, юрлицам или счетам (groupBy). Поля элемента: id, name, income, expense, opening, closing.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/reports/pnl

ПиУ за период

Скоуп:
reports:read
Функция тарифа:
«API»

Прибыли и убытки по разделам и категориям, помесячно. По умолчанию — по начислению (accrual): операция попадает в месяц своей даты начисления, а без неё — даты операции; cash — строго по дате операции. Выплаты собственнику идут после прибыли и в расходы не входят.

Параметры запроса

ПараметрТипОбязательныйОписание
dateFromstring (date)даДень ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Начало не позже конца, период не длиннее 10 лет. Формат ГГГГ-ММ-ДД.
dateTostring (date)даДень ГГГГ-ММ-ДД с 2000 года и не дальше пяти лет вперёд; иначе 422 validation_error. Начало не позже конца, период не длиннее 10 лет. Формат ГГГГ-ММ-ДД.
methodstringнетaccrual — по дате начисления (без неё — по дате операции), cash — по дате операции. Значения: cash, accrual. По умолчанию accrual.
unitIdstringнетТолько это направление.
legalEntityIdstringнетТолько это юрлицо.
projectIdstringнетТолько этот проект.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/reports/pnl" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -d dateFrom=2026-07-01 -d dateTo=2026-09-30 -d method=accrual

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "dateFrom": "2026-07-01T00:00:00.000Z",
    "dateTo": "2026-09-30T00:00:00.000Z",
    "method": "accrual",
    "months": [
      {
        "year": 2026,
        "month": 7,
        "key": "2026-07"
      },
      {
        "year": 2026,
        "month": 8,
        "key": "2026-08"
      },
      {
        "year": 2026,
        "month": 9,
        "key": "2026-09"
      }
    ],
    "sections": [
      {
        "sectionId": "cm2sec8d5k0018x1y3z5a7b9c",
        "name": "Выручка",
        "type": "INCOME",
        "order": 1,
        "lines": [
          {
            "categoryId": "cm2cat6b3j0017w9x2y4z6a8b",
            "categoryName": "Выручка от услуг",
            "type": "INCOME",
            "byMonth": {
              "2026-07": 81000000,
              "2026-08": 90500000,
              "2026-09": 98400000
            },
            "total": 269900000,
            "previousTotal": null
          }
        ],
        "totalsByMonth": {
          "2026-07": 81000000,
          "2026-08": 90500000,
          "2026-09": 98400000
        },
        "total": 269900000,
        "previousTotal": null
      }
    ],
    "incomeByMonth": {
      "2026-07": 81000000,
      "2026-08": 90500000,
      "2026-09": 98400000
    },
    "expenseByMonth": {
      "2026-07": 70200000,
      "2026-08": 71900000,
      "2026-09": 76550000
    },
    "profitByMonth": {
      "2026-07": 10800000,
      "2026-08": 18600000,
      "2026-09": 21850000
    },
    "totalIncome": 269900000,
    "totalExpense": 218650000,
    "totalProfit": 51250000,
    "withdrawalsByMonth": {
      "2026-07": 0,
      "2026-08": 10000000,
      "2026-09": 0
    },
    "totalWithdrawals": 10000000,
    "retainedByMonth": {
      "2026-07": 10800000,
      "2026-08": 8600000,
      "2026-09": 21850000
    },
    "totalRetained": 41250000,
    "comparison": null
  }
}

Поля ответа

ПолеТипОписание
dateFromstring (date-time)Первый день периода — полночь UTC. ISO 8601.
dateTostring (date-time)Последний день периода — полночь UTC. ISO 8601.
methodstringЗначения: cash, accrual.
monthsarray<object>Месяцы периода. Поля элемента: year, month, key.
sectionsarray<object>Разделы ПиУ со строками категорий. Поля элемента: sectionId, name, type, order, lines, totalsByMonth, total, previousTotal.
incomeByMonthobjectСумма по месяцам: ключ — «ГГГГ-ММ».
expenseByMonthobjectСумма по месяцам: ключ — «ГГГГ-ММ».
profitByMonthobjectСумма по месяцам: ключ — «ГГГГ-ММ».
totalIncomeintegerСумма в минимальных единицах валюты (тыйыны, копейки).
totalExpenseintegerСумма в минимальных единицах валюты (тыйыны, копейки).
totalProfitintegerСумма в минимальных единицах валюты (тыйыны, копейки).
withdrawalsByMonthobjectСумма по месяцам: ключ — «ГГГГ-ММ».
totalWithdrawalsintegerВыплаты собственнику — после прибыли, не в расходах.
retainedByMonthobjectСумма по месяцам: ключ — «ГГГГ-ММ».
totalRetainedintegerПрибыль минус выплаты собственнику.
comparisonobjectСравнение с прошлым периодом — во внешнем API всегда null. Может быть null.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Брони

Методы для систем бронирования (Voyz и другие): бронь и оплаты по ней передаются в Aynes, а Aynes ведёт их учёт — выручка датой выезда, предоплата до выезда — аванс гостя, наличная оплата — сразу приход в кассу, безналичная ждёт строку банковской выписки и склеивается человеком.

  • Бронь определяется парой source + externalId: код вашей системы (латиница, цифры, _ . -, до 40 символов) и номер брони в ней (без пробелов и /, до 120 символов). Код manual занят бронями, введёнными в Aynes вручную.
  • Скоупы — bookings:read и bookings:write, функция тарифа — «Брони». Ключ для системы бронирования выпускается на экране «Брони → Настройки броней».
  • Все записи идемпотентны: повтор того же запроса ничего не задваивает (см. «Идемпотентность и версии»).

get/bookings

Брони, принятые Aynes (сверка)

Скоуп:
bookings:read
Функция тарифа:
«Брони»; общему токену — ещё и «API»

Что Aynes принял — для сверки с вашей системой. Порядок — по времени изменения в Aynes; updatedSince отдаёт изменённое после прошлой сверки. reconciliation у брони: no_payments — оплат нет, awaiting — есть оплаты, ждущие выписку или кассу, reconciled — все оплаты в учёте.

Параметры запроса

ПараметрТипОбязательныйОписание
sourcestringнетКод системы бронирования; manual — брони, введённые вручную.
statusstringнетСтатус брони. Значения: pending, confirmed, checked_in, checked_out, cancelled, no_show.
updatedSincestring (date-time)нетИзменённые в Aynes с этого момента (ISO 8601 с поясом). ISO 8601.
pageintegerнетНомер страницы. По умолчанию 1.
pageSizeintegerнетЗаписей на странице. По умолчанию 50. Не больше 200.

Пример запроса

curl
curl -G "https://aynes.pro/api/v1/bookings" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -d source=voyz \
  --data-urlencode "updatedSince=2027-06-01T00:00:00+06:00"

Пример ответа · 200

json
{
  "ok": true,
  "data": [
    {
      "id": "cm2bkg8p5t0019z2a4b6c8d1e",
      "source": "voyz",
      "externalId": "B-2027-0714",
      "version": 7,
      "updatedAtSource": "2027-05-30T12:00:00.000Z",
      "status": "confirmed",
      "unit": {
        "id": "cm2unt6s3d0013j7k9m2n4p6q",
        "name": "Сон-Куль, юрточный лагерь"
      },
      "propertyRef": "camp-son-kul",
      "unitRef": "yurt-3",
      "categoryRef": "yurt",
      "channel": "instagram",
      "checkIn": "2027-07-14",
      "checkOut": "2027-07-17",
      "guests": 2,
      "guestName": "Айгерим",
      "currency": "KGS",
      "total": 1800000,
      "exchangeRate": "1",
      "baseAmount": 1800000,
      "channelCommission": 0,
      "nonRefundablePrepayment": true,
      "nonRefundableAmount": null,
      "note": "Конная прогулка 15.07",
      "cancelledAt": null,
      "paid": 900000,
      "refunded": 0,
      "reconciliation": "awaiting",
      "payments": [
        {
          "id": "cm2pay9q6u0020f3g5h7j9k2m",
          "externalId": "P-1",
          "kind": "prepayment",
          "method": "qr",
          "provider": "gopay",
          "amount": 900000,
          "fee": 18000,
          "currency": "KGS",
          "exchangeRate": "1",
          "baseAmount": 900000,
          "paidAt": "2027-06-01T04:21:00.000Z",
          "payoutRef": "gopay-payout-2027-06-02",
          "status": "awaiting_statement",
          "operationId": null,
          "linkedAt": null
        }
      ],
      "createdAt": "2027-05-30T12:00:03.118Z",
      "updatedAt": "2027-06-01T04:21:05.402Z"
    }
  ],
  "meta": {
    "page": 1,
    "pageSize": 50,
    "total": 1,
    "pages": 1
  }
}

Поля ответа

data — массив; поля элемента:

ПолеТипОписание
idstring
sourcestring
externalIdstring
versionintegerМожет быть null.
updatedAtSourcestring (date-time)ISO 8601. Может быть null.
statusstringЗначения: pending, confirmed, checked_in, checked_out, cancelled, no_show.
unitobjectМожет быть null. Поля: id, name.
propertyRefstringМожет быть null.
unitRefstringМожет быть null.
categoryRefstringМожет быть null.
channelstringМожет быть null.
checkInstring (date)Формат ГГГГ-ММ-ДД.
checkOutstring (date)Формат ГГГГ-ММ-ДД.
guestsintegerМожет быть null.
guestNamestringМожет быть null.
currencystring
totalintegerСумма в минимальных единицах валюты (тыйыны, копейки).
exchangeRatestring
baseAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
channelCommissionintegerСумма в минимальных единицах валюты (тыйыны, копейки).
nonRefundablePrepaymentboolean
nonRefundableAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки). Может быть null.
notestringМожет быть null.
cancelledAtstring (date-time)ISO 8601. Может быть null.
paidintegerПринятые оплаты в валюте брони (без отменённых).
refundedintegerВозвраты в валюте брони.
reconciliationstringЗначения: no_payments, awaiting, reconciled.
paymentsarray<BookingPayment>
createdAtstring (date-time)ISO 8601.
updatedAtstring (date-time)ISO 8601.
meta.pageintegerНомер страницы, с 1.
meta.pageSizeintegerЗаписей на странице.
meta.totalintegerВсего записей под фильтры.
meta.pagesintegerВсего страниц.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/bookings/{source}/{externalId}

Одна бронь с оплатами

Скоуп:
bookings:read
Функция тарифа:
«Брони»; общему токену — ещё и «API»

Чужая и несуществующая — 404.

Параметры пути

ПараметрТипОбязательныйОписание
sourcestringдаКод системы: voyz, booking_com, ostrovok… Код manual занят бронями, введёнными вручную в Aynes (D-208): запись с ним — 422.
externalIdstringдаНомер брони в системе.

Пример запроса

curl
curl "https://aynes.pro/api/v1/bookings/voyz/B-2027-0714" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2bkg8p5t0019z2a4b6c8d1e",
    "source": "voyz",
    "externalId": "B-2027-0714",
    "version": 7,
    "updatedAtSource": "2027-05-30T12:00:00.000Z",
    "status": "confirmed",
    "unit": {
      "id": "cm2unt6s3d0013j7k9m2n4p6q",
      "name": "Сон-Куль, юрточный лагерь"
    },
    "propertyRef": "camp-son-kul",
    "unitRef": "yurt-3",
    "categoryRef": "yurt",
    "channel": "instagram",
    "checkIn": "2027-07-14",
    "checkOut": "2027-07-17",
    "guests": 2,
    "guestName": "Айгерим",
    "currency": "KGS",
    "total": 1800000,
    "exchangeRate": "1",
    "baseAmount": 1800000,
    "channelCommission": 0,
    "nonRefundablePrepayment": true,
    "nonRefundableAmount": null,
    "note": "Конная прогулка 15.07",
    "cancelledAt": null,
    "paid": 900000,
    "refunded": 0,
    "reconciliation": "awaiting",
    "payments": [
      {
        "id": "cm2pay9q6u0020f3g5h7j9k2m",
        "externalId": "P-1",
        "kind": "prepayment",
        "method": "qr",
        "provider": "gopay",
        "amount": 900000,
        "fee": 18000,
        "currency": "KGS",
        "exchangeRate": "1",
        "baseAmount": 900000,
        "paidAt": "2027-06-01T04:21:00.000Z",
        "payoutRef": "gopay-payout-2027-06-02",
        "status": "awaiting_statement",
        "operationId": null,
        "linkedAt": null
      }
    ],
    "createdAt": "2027-05-30T12:00:03.118Z",
    "updatedAt": "2027-06-01T04:21:05.402Z"
  }
}

Поля ответа

ПолеТипОписание
idstring
sourcestring
externalIdstring
versionintegerМожет быть null.
updatedAtSourcestring (date-time)ISO 8601. Может быть null.
statusstringЗначения: pending, confirmed, checked_in, checked_out, cancelled, no_show.
unitobjectМожет быть null. Поля: id, name.
propertyRefstringМожет быть null.
unitRefstringМожет быть null.
categoryRefstringМожет быть null.
channelstringМожет быть null.
checkInstring (date)Формат ГГГГ-ММ-ДД.
checkOutstring (date)Формат ГГГГ-ММ-ДД.
guestsintegerМожет быть null.
guestNamestringМожет быть null.
currencystring
totalintegerСумма в минимальных единицах валюты (тыйыны, копейки).
exchangeRatestring
baseAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
channelCommissionintegerСумма в минимальных единицах валюты (тыйыны, копейки).
nonRefundablePrepaymentboolean
nonRefundableAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки). Может быть null.
notestringМожет быть null.
cancelledAtstring (date-time)ISO 8601. Может быть null.
paidintegerПринятые оплаты в валюте брони (без отменённых).
refundedintegerВозвраты в валюте брони.
reconciliationstringЗначения: no_payments, awaiting, reconciled.
paymentsarray<BookingPayment>
createdAtstring (date-time)ISO 8601.
updatedAtstring (date-time)ISO 8601.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
404not_foundБронь не найдена
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

put/bookings/{source}/{externalId}

Создать или обновить бронь (идемпотентно)

Скоуп:
bookings:write
Функция тарифа:
«Брони»; общему токену — ещё и «API»

Бронь передаётся целиком при каждом изменении: создании, подтверждении, переносе дат, заезде, выезде, отмене.

  • status: pending — ждёт подтверждения, confirmed — подтверждена, checked_in — гость заехал, checked_out — выехал, cancelled — отменена, no_show — гость не приехал.
  • version растёт при каждом изменении брони у вас. Старее той, что в Aynes, — 409 conflict с details.currentVersion; та же версия с другими данными — тоже 409. Без версии сравнивается updatedAt; без обоих — побеждает последний запрос.
  • Выручка признаётся всей суммой датой выезда; предоплата до выезда — аванс гостя. Смена выезда, статуса или отмены переносит дату начисления уже проведённого; проведённое по курсу не пересчитывается.
  • guestName — по желанию; телефон и документы гостя не передаются.
  • propertyRef — ваш объект (лагерь, гостевой дом): в Aynes он появляется сам с первой брони, направление ему выбирают в «Настройках броней».

Параметры пути

ПараметрТипОбязательныйОписание
sourcestringдаКод системы: voyz, booking_com, ostrovok… Код manual занят бронями, введёнными вручную в Aynes (D-208): запись с ним — 422.
externalIdstringдаНомер брони в системе.

Тело запроса

ПолеТипОбязательныйОписание
versionintegerнетРастёт при каждом изменении брони в источнике; старая версия — 409 conflict. Может быть null.
updatedAtstring (date-time)нетВремя изменения в источнике — если версии нет. ISO 8601. Может быть null.
statusstringдаЗначения: pending, confirmed, checked_in, checked_out, cancelled, no_show.
propertyRefstringнетОбъект (лагерь, гостевой дом) — сопоставляется с направлением Aynes в настройках броней. Может быть null.
unitRefstringнетМожет быть null.
categoryRefstringнетМожет быть null.
channelstringнетМожет быть null.
checkInstring (date)даФормат ГГГГ-ММ-ДД.
checkOutstring (date)даФормат ГГГГ-ММ-ДД.
guestsintegerнетМожет быть null.
guestNamestringнетПо желанию; телефон и документы не передаются. Может быть null.
currencystringда
totalintegerдаСумма в минимальных единицах валюты (тыйыны, копейки).
channelCommissionintegerнетСумма в минимальных единицах валюты (тыйыны, копейки).
nonRefundablePrepaymentbooleanнет
nonRefundableAmountintegerнетСумма в минимальных единицах валюты (тыйыны, копейки). Может быть null.
notestringнетДо 500 символов. Может быть null.
cancelledAtstring (date-time)нетКогда бронь отменили; без него — момент, когда отмена пришла. ISO 8601. Может быть null.

Пример запроса

curl
curl -X PUT "https://aynes.pro/api/v1/bookings/voyz/B-2027-0714" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "version": 7,
    "updatedAt": "2027-05-30T18:00:00+06:00",
    "status": "confirmed",
    "propertyRef": "camp-son-kul",
    "unitRef": "yurt-3",
    "categoryRef": "yurt",
    "channel": "instagram",
    "checkIn": "2027-07-14",
    "checkOut": "2027-07-17",
    "guests": 2,
    "guestName": "Айгерим",
    "currency": "KGS",
    "total": 1800000,
    "channelCommission": 0,
    "nonRefundablePrepayment": true,
    "note": "Конная прогулка 15.07"
  }'

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2bkg8p5t0019z2a4b6c8d1e",
    "source": "voyz",
    "externalId": "B-2027-0714",
    "version": 7,
    "updatedAtSource": "2027-05-30T12:00:00.000Z",
    "status": "confirmed",
    "unit": {
      "id": "cm2unt6s3d0013j7k9m2n4p6q",
      "name": "Сон-Куль, юрточный лагерь"
    },
    "propertyRef": "camp-son-kul",
    "unitRef": "yurt-3",
    "categoryRef": "yurt",
    "channel": "instagram",
    "checkIn": "2027-07-14",
    "checkOut": "2027-07-17",
    "guests": 2,
    "guestName": "Айгерим",
    "currency": "KGS",
    "total": 1800000,
    "exchangeRate": "1",
    "baseAmount": 1800000,
    "channelCommission": 0,
    "nonRefundablePrepayment": true,
    "nonRefundableAmount": null,
    "note": "Конная прогулка 15.07",
    "cancelledAt": null,
    "paid": 0,
    "refunded": 0,
    "reconciliation": "no_payments",
    "payments": [],
    "createdAt": "2027-05-30T12:00:03.118Z",
    "updatedAt": "2027-06-01T04:21:05.402Z"
  },
  "meta": {
    "created": true,
    "changed": true
  }
}

Поля ответа

ПолеТипОписание
idstring
sourcestring
externalIdstring
versionintegerМожет быть null.
updatedAtSourcestring (date-time)ISO 8601. Может быть null.
statusstringЗначения: pending, confirmed, checked_in, checked_out, cancelled, no_show.
unitobjectМожет быть null. Поля: id, name.
propertyRefstringМожет быть null.
unitRefstringМожет быть null.
categoryRefstringМожет быть null.
channelstringМожет быть null.
checkInstring (date)Формат ГГГГ-ММ-ДД.
checkOutstring (date)Формат ГГГГ-ММ-ДД.
guestsintegerМожет быть null.
guestNamestringМожет быть null.
currencystring
totalintegerСумма в минимальных единицах валюты (тыйыны, копейки).
exchangeRatestring
baseAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
channelCommissionintegerСумма в минимальных единицах валюты (тыйыны, копейки).
nonRefundablePrepaymentboolean
nonRefundableAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки). Может быть null.
notestringМожет быть null.
cancelledAtstring (date-time)ISO 8601. Может быть null.
paidintegerПринятые оплаты в валюте брони (без отменённых).
refundedintegerВозвраты в валюте брони.
reconciliationstringЗначения: no_payments, awaiting, reconciled.
paymentsarray<BookingPayment>
createdAtstring (date-time)ISO 8601.
updatedAtstring (date-time)ISO 8601.
meta.createdbooleanЗапись создана этим запросом.
meta.changedbooleanЗапрос что-то изменил; повтор того же тела — false.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbidden, read_onlyУ токена нет нужного скоупа, тариф без нужной функции или (для записи) организация в режиме чтения
409conflictСтарая версия брони
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

get/bookings/{source}/{externalId}/payments/{paymentId}

Одна оплата брони

Скоуп:
bookings:read
Функция тарифа:
«Брони»; общему токену — ещё и «API»

Одна оплата и её состояние: awaiting_statement — безналичная, ждёт строку выписки; awaiting_cash_account — наличная, касса не выбрана или в другой валюте; matched — склеена с выпиской; posted — проведена в кассу; cancelled — отменена.

Параметры пути

ПараметрТипОбязательныйОписание
sourcestringдаКод системы: voyz, booking_com, ostrovok… Код manual занят бронями, введёнными вручную в Aynes (D-208): запись с ним — 422.
externalIdstringдаНомер брони в системе.
paymentIdstringдаНомер оплаты в системе бронирования.

Пример запроса

curl
curl "https://aynes.pro/api/v1/bookings/voyz/B-2027-0714/payments/P-1" \
  -H "Authorization: Bearer $AYNES_TOKEN"

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2pay9q6u0020f3g5h7j9k2m",
    "externalId": "P-1",
    "kind": "prepayment",
    "method": "qr",
    "provider": "gopay",
    "amount": 900000,
    "fee": 18000,
    "currency": "KGS",
    "exchangeRate": "1",
    "baseAmount": 900000,
    "paidAt": "2027-06-01T04:21:00.000Z",
    "payoutRef": "gopay-payout-2027-06-02",
    "status": "awaiting_statement",
    "operationId": null,
    "linkedAt": null
  }
}

Поля ответа

ПолеТипОписание
idstring
externalIdstringНомер оплаты в системе бронирования.
kindstringЗначения: prepayment, payment, refund.
methodstringЗначения: qr, card, transfer, cash, ota.
providerstringМожет быть null.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
feeintegerКомиссия, удержанная из этой оплаты: на счёт пришло amount − fee.
currencystring
exchangeRatestring
baseAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
paidAtstring (date-time)ISO 8601.
payoutRefstringНомер выплаты пачкой у провайдера. Может быть null.
statusstringawaiting_statement — безналичная, ждёт строку выписки; awaiting_cash_account — наличная, касса не выбрана; matched — склеена с выпиской; posted — проведена в кассу; cancelled — отменена. Значения: awaiting_statement, awaiting_cash_account, matched, posted, cancelled.
operationIdstringОперация Aynes, которой проведена оплата. Может быть null.
linkedAtstring (date-time)ISO 8601. Может быть null.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbiddenУ токена нет нужного скоупа или тариф без нужной функции
404not_foundОплата не найдена
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

put/bookings/{source}/{externalId}/payments/{paymentId}

Оплата по брони (идемпотентно по номеру оплаты)

Скоуп:
bookings:write
Функция тарифа:
«Брони»; общему токену — ещё и «API»
  • paymentId — номер оплаты у вас: повтор с тем же номером обновляет, а не задваивает. Бронь должна быть передана раньше, иначе 404.
  • Наличная (method = cash) сразу становится приходом в кассу из «Настроек броней»; возврат наличными — расходом из неё же.
  • Безналичная (qr, card, transfer, ota) операций не создаёт: деньги придут банковской выпиской, и человек в Aynes склеит оплату со строкой выписки — так деньги не учитываются дважды. Выплата пачкой склеивается по общему payoutRef. После склейки придёт вебхук booking.reconciled.
  • fee — комиссия эквайринга или площадки, удержанная из этой оплаты: на счёт придёт amount − fee. У возврата комиссии нет.
  • status = cancelled — оплата не состоялась или отменена у вас. Отмена проведённой наличной снимает её операции; отмена склеенной — 409.
  • Проведённую или склеенную оплату поменять нельзя (сумма, валюта, способ, вид, комиссия, дата) — 409; provider и payoutRef меняются всегда.

Параметры пути

ПараметрТипОбязательныйОписание
sourcestringдаКод системы: voyz, booking_com, ostrovok… Код manual занят бронями, введёнными вручную в Aynes (D-208): запись с ним — 422.
externalIdstringдаНомер брони в системе.
paymentIdstringдаНомер оплаты в системе бронирования.

Тело запроса

ПолеТипОбязательныйОписание
kindstringдаЗначения: prepayment, payment, refund.
methodstringдаcash — проводится в кассу сразу; остальное ждёт выписку. Значения: qr, card, transfer, cash, ota.
providerstringнетМожет быть null.
amountintegerдаСколько заплатил гость (возврат — сколько вернули).
feeintegerнетКомиссия эквайринга или площадки, удержанная из этой оплаты.
currencystringда
paidAtstring (date-time)даISO 8601.
payoutRefstringнетМожет быть null.
statusstringнетЗначения: succeeded, cancelled. По умолчанию succeeded.

Пример запроса

curl
curl -X PUT "https://aynes.pro/api/v1/bookings/voyz/B-2027-0714/payments/P-1" \
  -H "Authorization: Bearer $AYNES_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "prepayment",
    "method": "qr",
    "provider": "gopay",
    "amount": 900000,
    "fee": 18000,
    "currency": "KGS",
    "paidAt": "2027-06-01T10:21:00+06:00",
    "payoutRef": "gopay-payout-2027-06-02"
  }'

Пример ответа · 200

json
{
  "ok": true,
  "data": {
    "id": "cm2pay9q6u0020f3g5h7j9k2m",
    "externalId": "P-1",
    "kind": "prepayment",
    "method": "qr",
    "provider": "gopay",
    "amount": 900000,
    "fee": 18000,
    "currency": "KGS",
    "exchangeRate": "1",
    "baseAmount": 900000,
    "paidAt": "2027-06-01T04:21:00.000Z",
    "payoutRef": "gopay-payout-2027-06-02",
    "status": "awaiting_statement",
    "operationId": null,
    "linkedAt": null
  },
  "meta": {
    "created": true,
    "changed": true
  }
}

Поля ответа

ПолеТипОписание
idstring
externalIdstringНомер оплаты в системе бронирования.
kindstringЗначения: prepayment, payment, refund.
methodstringЗначения: qr, card, transfer, cash, ota.
providerstringМожет быть null.
amountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
feeintegerКомиссия, удержанная из этой оплаты: на счёт пришло amount − fee.
currencystring
exchangeRatestring
baseAmountintegerСумма в минимальных единицах валюты (тыйыны, копейки).
paidAtstring (date-time)ISO 8601.
payoutRefstringНомер выплаты пачкой у провайдера. Может быть null.
statusstringawaiting_statement — безналичная, ждёт строку выписки; awaiting_cash_account — наличная, касса не выбрана; matched — склеена с выпиской; posted — проведена в кассу; cancelled — отменена. Значения: awaiting_statement, awaiting_cash_account, matched, posted, cancelled.
operationIdstringОперация Aynes, которой проведена оплата. Может быть null.
linkedAtstring (date-time)ISO 8601. Может быть null.
meta.createdbooleanЗапись создана этим запросом.
meta.changedbooleanЗапрос что-то изменил; повтор того же тела — false.

Ошибки

HTTPКодОписание
401unauthorizedТокен не передан, отозван или истёк
403forbidden, read_onlyУ токена нет нужного скоупа, тариф без нужной функции или (для записи) организация в режиме чтения
404not_foundБронь не найдена
409conflictОплата уже в учёте
422validation_errorОшибка валидации
429rate_limitedПревышен лимит запросов; см. Retry-After

Вебхуки

Подписка

  • Подписку заводит владелец или администратор: Настройки → API и вебхуки → Вебхуки — адрес HTTPS, события и описание. Подписок — не больше 20 на организацию.
  • Секрет подписи whs_… показывается один раз при создании — сохраните его рядом с обработчиком.
  • Вебхуки — функция тарифа «API»: на тарифе без неё подписки сохраняются, но события по ним не выпускаются, пока функция не вернётся.
  • Адрес — только https:// и только во внешнюю сеть: адреса localhost и частных сетей не сохранятся. Перенаправления не выполняются — укажите конечный адрес.
  • Выключенная подписка событий не получает; уже поставленные доставки по ней завершаются ошибкой «Подписка выключена».

События

СобытиеКогда приходит
operation.createdНовая операция. Появилась операция: введена вручную, загружена из банковской выписки (событие на каждую строку) или записана платежом сотрудника из мини-приложения Telegram.
payment_request.status_changedСмена статуса заявки. Заявка на оплату сменила статус: отправлена на согласование, согласована или отклонена (решением последнего шага маршрута), оплачена, отменена.
payment_request.updatedИзменение заявки. Изменены поля заявки, которая ждёт согласования (сумма, назначение, категория, срок…), или срок либо счёт заявки перенесены в платёжном календаре.
sync.failedОшибка синхронизации банка. Загрузка банковской выписки не удалась: обмен с банком, письмо с выпиской или загрузка файла.
booking.reconciledОплата брони найдена в выписке. Оплата брони нашлась в банковской выписке: человек склеил её со строкой выписки на экране «Брони». Событие на каждую оплату, в том числе в выплате пачкой.

Тело каждой доставки — JSON одного вида: имя события, организация, момент события и данные:

json
{
  "event": "operation.created",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "id": "cm2opr9e6k0009m1n3p5q7r9s",
    "type": "OUTCOME",
    "status": "ACTUAL",
    "amount": 4500000,
    "currency": "KGS",
    "baseAmount": 4500000,
    "operationDate": "2026-10-03T00:00:00.000Z",
    "unitId": "cm2unt5r2c0003h6j8k1m3n5p",
    "bankAccountId": "cm2acc9u6e0004y5z7a9b2c4d",
    "contractorId": "cm2cnt3y0g0006n7p9q2r4s6t",
    "categoryId": null,
    "projectId": null,
    "description": "Аренда офиса за октябрь 2026",
    "source": "BANK_STATEMENT"
  }
}
  • В data — идентификаторы и главные поля; полную запись перечитайте методом API.
  • Порядок доставок не гарантирован, а одно событие может прийти дважды (повтор после таймаута): отбрасывайте повторы по заголовку X-Webhook-Delivery.

operation.created

Появилась операция: введена вручную, загружена из банковской выписки (событие на каждую строку) или записана платежом сотрудника из мини-приложения Telegram.

В теле — поля самой операции, у связей — только идентификаторы. Названия категории, счёта и контрагента — в справочниках или в GET /operations.

Поля data
ПолеТипОписание
idstringИдентификатор операции
typestringINCOME, OUTCOME или TRANSFER
statusstringACTUAL или PLANNED
amountintegerСумма в минимальных единицах валюты счёта
currencystringВалюта счёта, ISO 4217
baseAmountintegerЭквивалент в базовой валюте организации
operationDatestring (date-time)Дата операции
unitIdstring | nullНаправление; null — направление ещё не поставлено
bankAccountIdstringСчёт или касса
contractorIdstring | nullКонтрагент
categoryIdstring | nullКатегория
projectIdstring | nullПроект
descriptionstring | nullНазначение
sourcestringОткуда операция: MANUAL, BANK_STATEMENT, MINIAPP и другие
Пример тела доставки
json
{
  "event": "operation.created",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "id": "cm2opr9e6k0009m1n3p5q7r9s",
    "type": "OUTCOME",
    "status": "ACTUAL",
    "amount": 4500000,
    "currency": "KGS",
    "baseAmount": 4500000,
    "operationDate": "2026-10-03T00:00:00.000Z",
    "unitId": "cm2unt5r2c0003h6j8k1m3n5p",
    "bankAccountId": "cm2acc9u6e0004y5z7a9b2c4d",
    "contractorId": "cm2cnt3y0g0006n7p9q2r4s6t",
    "categoryId": null,
    "projectId": null,
    "description": "Аренда офиса за октябрь 2026",
    "source": "BANK_STATEMENT"
  }
}

payment_request.status_changed

Заявка на оплату сменила статус: отправлена на согласование, согласована или отклонена (решением последнего шага маршрута), оплачена, отменена.

  • Решение промежуточного шага маршрута статус не меняет — заявка остаётся PENDING, события нет.
  • PAID приходит, когда заявку склеили с операцией оплаты: сам матчер или человек, подтвердивший связку.
Поля data
ПолеТипОписание
idstringИдентификатор заявки — перечитайте её GET /payment-requests/{id}
fromstringПрежний статус: DRAFT, PENDING, APPROVED
tostringНовый статус: PENDING, APPROVED, REJECTED, PAID, CANCELLED
actorIdstringКто сменил статус; нет у оплаты, склеенной автоматически
operationIdstringТолько у PAID: операция оплаты
Пример тела доставки
json
{
  "event": "payment_request.status_changed",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "id": "cm2req1g8m0010t2v4w6x8y1z",
    "from": "PENDING",
    "to": "APPROVED",
    "actorId": "cm2usr4j1p0012g4h6j8k1m3n"
  }
}

payment_request.updated

Изменены поля заявки, которая ждёт согласования (сумма, назначение, категория, срок…), или срок либо счёт заявки перенесены в платёжном календаре.

Событие несёт только список изменённых полей, без значений: новые значения — в GET /payment-requests/{id}.

Поля data
ПолеТипОписание
idstringИдентификатор заявки
changesarray<string>Имена изменённых полей, как в ответе API: amount, dueDate, bankAccountId…
actorIdstringКто изменил
viastringcalendar — перенос в платёжном календаре; нет — правка карточки заявки
Пример тела доставки
json
{
  "event": "payment_request.updated",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "id": "cm2req1g8m0010t2v4w6x8y1z",
    "changes": [
      "dueDate"
    ],
    "via": "calendar",
    "actorId": "cm2usr3h9n0011a3b5c7d9e1f"
  }
}

sync.failed

Загрузка банковской выписки не удалась: обмен с банком, письмо с выпиской или загрузка файла.

Поля data
ПолеТипОписание
syncRunIdstringИдентификатор прогона загрузки
integrationIdstring | nullПодключение банка; null — загрузка без подключения
connectorCodestringКод коннектора банка, например MBANK_1C
errorstringТекст ошибки, до 500 символов
Пример тела доставки
json
{
  "event": "sync.failed",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "syncRunId": "cm2syn2r7v0021h4j6k8m1n3p",
    "integrationId": null,
    "connectorCode": "MBANK_1C",
    "error": "Файл не похож на выписку 1С: нет секции СекцияРасчСчет"
  }
}

booking.reconciled

Оплата брони нашлась в банковской выписке: человек склеил её со строкой выписки на экране «Брони». Событие на каждую оплату, в том числе в выплате пачкой.

Поля data
ПолеТипОписание
sourcestringКод системы бронирования
bookingExternalIdstringНомер брони в системе
paymentExternalIdstringНомер оплаты в системе
amountintegerСумма оплаты в минимальных единицах
feeintegerУдержанная комиссия
currencystringВалюта оплаты
operationIdstringОперация Aynes — строка выписки
operationDatestring (date-time)Дата строки выписки
Пример тела доставки
json
{
  "event": "booking.reconciled",
  "tenantId": "cm2tnt3p0b0000a1b2c3d4e5f",
  "occurredAt": "2026-10-08T09:30:00.000Z",
  "data": {
    "source": "voyz",
    "bookingExternalId": "B-2027-0714",
    "paymentExternalId": "P-1",
    "amount": 900000,
    "fee": 18000,
    "currency": "KGS",
    "operationId": "cm2opr3t8w0022k5m7n9p2q4r",
    "operationDate": "2027-06-02T00:00:00.000Z"
  }
}

Запрос к вашему адресу

Aynes шлёт POST на адрес подписки с заголовками:

ЗаголовокЗначение
Content-Typeapplication/json
User-Agentfinance-saas-webhooks/1
X-Webhook-EventИмя события, например operation.created
X-Webhook-DeliveryИдентификатор доставки — один у всех попыток одной доставки
X-Webhook-Signaturesha256= и HMAC-SHA256 тела секретом подписки в hex

Проверка подписи

  1. Прочитайте тело запроса как есть, байтами, — до разбора JSON. Пересобранный JSON может отличаться порядком ключей и пробелами, и подпись не сойдётся.
  2. Посчитайте HMAC-SHA256 тела; ключ — секрет подписки whs_… целиком.
  3. Запишите результат в hex строчными буквами и добавьте sha256= в начало.
  4. Сравните с заголовком X-Webhook-Signature функцией сравнения за постоянное время. Не совпало — ответьте 401 и ничего не делайте.

Пример для самопроверки: секрет whs_test_secret и тело ниже дают подпись sha256=f24234cc39b57e398bf704378fb6a43f8421902c8bc4177fac84fd105dfdd421.

json
{"event":"sync.failed","tenantId":"t1","occurredAt":"2026-10-08T09:30:00.000Z","data":{"error":"test"}}
Node.js (Express)
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const SECRET = process.env.AYNES_WEBHOOK_SECRET; // whs_…
const app = express();

// Подпись считается от тела как есть — читаем его байтами, до JSON.parse.
app.post("/aynes/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + createHmac("sha256", SECRET).update(req.body).digest("hex");
  const received = req.get("X-Webhook-Signature") ?? "";
  const valid =
    received.length === expected.length &&
    timingSafeEqual(Buffer.from(received), Buffer.from(expected));
  if (!valid) return res.status(401).end();

  const delivery = req.get("X-Webhook-Delivery"); // повтор той же доставки — отбросить
  const { event, data } = JSON.parse(req.body.toString("utf8"));
  // Ответьте сразу, долгую обработку — после ответа.
  res.status(204).end();
  handle(delivery, event, data);
});
PHP
<?php
$secret = getenv('AYNES_WEBHOOK_SECRET'); // whs_…
$body = file_get_contents('php://input');  // тело как есть, до json_decode

$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$delivery = $_SERVER['HTTP_X_WEBHOOK_DELIVERY'] ?? ''; // повтор той же доставки — отбросить
$payload = json_decode($body, true);
// $payload['event'], $payload['data'] …
http_response_code(204);
Python (Flask)
import hashlib
import hmac
import json
import os

from flask import Flask, abort, request

SECRET = os.environ["AYNES_WEBHOOK_SECRET"].encode()  # whs_…
app = Flask(__name__)


@app.post("/aynes/webhook")
def aynes_webhook():
    body = request.get_data()  # тело как есть, до разбора JSON
    expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    received = request.headers.get("X-Webhook-Signature", "")
    if not hmac.compare_digest(expected, received):
        abort(401)

    delivery = request.headers.get("X-Webhook-Delivery")  # повтор — отбросить
    payload = json.loads(body)
    # payload["event"], payload["data"] …
    return "", 204

Повторы и успешная доставка

  • Доставка успешна, если ваш адрес ответил кодом 2xx в течение 10 секунд. Тело ответа не читается.
  • Любой другой код, перенаправление 3xx, таймаут или ошибка соединения — неудача, и доставка повторяется.
  • Всего до 6 попыток: первая и 5 повторов. Паузы растут вдвое со случайным разбросом: около 30–60 секунд, 1–2, 2–4, 4–8 и 8–16 минут — все попытки укладываются примерно в полчаса.
  • После последней неудачной попытки доставка помечается «Не доставлено» и больше не повторяется.
  • Отвечайте сразу, а долгую обработку делайте после ответа или в своей очереди.

Журнал доставок

На экране «Вебхуки» — последние 50 доставок: событие, состояние («Ждёт», «Доставлено», «Не доставлено»), число попыток, код ответа вашего сервера и текст ошибки. У каждой подписки — время и код последней доставки. По журналу удобно отлаживать обработчик: идентификатор доставки в журнале — тот же, что в заголовке X-Webhook-Delivery.

Изменения API

ДатаЧто изменилось
2026-10-17Создание операций: POST /operations, скоуп operations:write, заголовок Idempotency-Key; повтор с тем же ключом — 200 и meta.repeated.
2026-10-08Документация разработчика на этой странице. Событие booking.reconciled теперь доставляется: суммы в его теле — числа.
2026-10-07Брони и оплаты броней: GET /bookings, GET и PUT /bookings/{source}/{externalId}, GET и PUT …/payments/{paymentId}, скоупы bookings:read и bookings:write, код ошибки conflict, событие booking.reconciled. Ключ для системы бронирования работает на тарифе с бронями без функции «API».
2026-09-27Участок работ проекта: фильтр directionId в GET /operations и поле directionId в теле POST /payment-requests.
2026-09-23Чтение заявок: GET /payment-requests, GET /payment-requests/{id}, скоуп payment-requests:read.
2026-09-22Событие payment_request.updated.
2026-09-17Версия v1: токены и скоупы, GET /me, GET /operations, POST /payment-requests, справочники, отчёты ДДС и ПиУ, вебхуки operation.created, payment_request.status_changed, sync.failed.