API YONA для бизнеса
Салон может подключить к YONA свою программу — 1С, собственную CRM, бота или сайт. Есть две стороны: вы спрашиваете нас (REST-запросы) и мы сами стучимся к вам, когда что-то произошло (вебхуки). Спрашивать каждую минуту не нужно и не стоит: подписка на события бесплатна и не расходует предел запросов.
Кому доступно
API и вебхуки входят в тарифы PRO и ENTERPRISE. На пробном периоде доступна песочница: ключ выпускается, запросы работают с низким пределом, тестовое событие уходит по кнопке, но настоящие записи вебхуками не рассылаются. На тарифе SOLO API не работает.
Ключ
Ключ выпускает владелец салона в разделе «Интеграции». Ключ показывается один раз — мы храним только его отпечаток и восстановить его не сможем. У ключа есть права: выдавайте ровно те, что нужны программе. Отозванный ключ перестаёт работать со следующего запроса.
Как обращаться
Все маршруты начинаются с /api/v1. Ключ передаётся заголовком Authorization. Проверить ключ — GET /api/v1/me: вернётся ваш салон, права ключа и предел запросов.
curl -H "Authorization: Bearer yona_ab12cd34_…" \
https://yona.uz/api/v1/servicesПределы
PRO — 120 запросов в минуту на ключ, ENTERPRISE — 600, пробный период — 20. Превышение отвечает 429 и заголовком Retry-After. Счётчика оплаченных вызовов нет: мы не берём денег за каждый запрос.
Маршруты
| HTTP | Право | Что делает |
|---|---|---|
| GET /api/v1/me | catalog:read | Салон, права ключа и предел запросов |
| GET /api/v1/services | catalog:read | Услуги с ценой и длительностью (?archived=1 — вместе с архивными) |
| GET /api/v1/masters | catalog:read | Сотрудники и кто из них принимает клиентов |
| GET /api/v1/slots?serviceId&date&masterId | schedule:read | Свободное время на день салона (YYYY-MM-DD) |
| GET /api/v1/bookings?from&to&status&masterId&customerId&limit&cursor | bookings:read | Записи страницами |
| GET /api/v1/bookings/{id} | bookings:read | Одна запись |
| POST /api/v1/bookings | bookings:write | Создать запись: serviceId, startTime и либо customerId, либо customerName с customerPhone |
| POST /api/v1/bookings/{id}/cancel | bookings:write | Отменить запись |
| GET /api/v1/customers?limit&cursor | customers:read | Клиенты салона страницами |
Вебхуки
Адрес добавляет владелец салона там же, в «Интеграциях». Мы шлём POST с телом JSON. Отвечайте кодом 2xx как можно быстрее: тело ответа мы не читаем, а работу лучше отложить в свою очередь. Адрес должен быть https и доступен из интернета — во внутреннюю сеть мы не ходим и по перенаправлениям не идём.
События
booking.created— Новая записьbooking.confirmed— Запись подтвержденаbooking.changed— Запись перенесенаbooking.cancelled— Запись отмененаbooking.completed— Визит завершён
Подпись
Каждая отправка подписана вашим секретом. В заголовке X-Yona-Signature приходит t=<время>,v1=<подпись>, где подпись — HMAC-SHA256 по строке «время.тело». Проверяйте её и отвергайте всё, что старше пяти минут: без этого запись можно повторить чужими руками.
Не проверять подпись — значит принимать записи от кого угодно, кто узнал ваш адрес.
const signature = request.headers['x-yona-signature']; // t=1758297600,v1=…
const [, timestamp, given] = /^t=(\d+),v1=([0-9a-f]+)$/.exec(signature) ?? [];
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return reject();
const expected = crypto
.createHmac('sha256', YOUR_WEBHOOK_SECRET)
.update(timestamp + '.' + rawBody) // the raw body, before JSON.parse
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(given, 'hex'))) return reject();Повторы
Если вы ответили не 2xx или не ответили вовсе, мы повторим до шести раз с растущей паузой. Одно и то же событие может прийти дважды — сверяйте заголовок X-Yona-Delivery и обрабатывайте повтор как уже сделанное. Адрес, который отказывает пятнадцать доставок подряд, мы отключаем и пишем об этом в журнал на экране.
Отказы
Ответ с ошибкой — это JSON с полями error и code. Ветвитесь по code: текст в error написан для человека, читающего лог, и может измениться.
Если что-то не так
Журнал отправок на экране «Интеграции» показывает каждую доставку: время, событие, ответ вашего сервера и кнопку «Повторить». С него и начинайте.