API ElixTime
Читайте расписание, клиентов и услуги салона, создавайте и отменяйте записи из своей системы. Тот же движок, что за виджетом записи, — значит и те же правила: занятый слот не займётся дважды, уведомления уйдут, лимиты тарифа сработают.
https://api.elixtime.ru/v1/apiНачало работы
- Откройте Настройки → Интеграции → Ключи API в админке салона.
- Выпустите ключ и сразу скопируйте его — второй раз он не покажется.
- Проверьте связь запросом
/ping.
curl https://api.elixtime.ru/v1/api/ping \ -H "X-Api-Key: elx_ваш_ключ"
Ключи выпускает только владелец салона. Управляющие и сотрудники этот раздел не видят.
Аутентификация
Каждый запрос несёт заголовок X-Api-Key. Ключ принадлежит салону, а не человеку: увольнение администратора не ломает интеграцию.
X-Api-Key: elx_cyAidJRFkZbRD840b1reIGsPZyiIzkKTiTwaYLj5
На сервере хранится только SHA-256 хеш ключа — восстановить его невозможно ни нам, ни вам. Потеряли — выпустите новый и отзовите старый.
Права
| Область | Что разрешено |
|---|---|
read | Все методы GET |
read_write | Плюс создание и отмена записей |
Права «на всё» не бывает намеренно: интеграции с аналитикой незачем уметь отменять визиты.
Форматы и единицы
Списки
Каждый список отвечает одинаковым конвертом, чтобы не гадать про пагинацию:
{ "items": [ … ], "total": 128, "limit": 50, "offset": 0 }Время
Везде ISO 8601 со смещением. Записи отдаются в часовом поясе салона — его отдаёт /ping. На вход принимается любое корректное ISO-время, включая Z.
Деньги
Здесь легко ошибиться, поэтому прямо: price и full_price — в рублях, а paid_amount_kopecks и refunded_amount_kopecks — в копейках. price — сколько платит клиент, с учётом промокода и баллов, — одинаково в API и во всех вебхуках о записи и оплате. full_price — полная цена визита. Они различаются у визита по абонементу и подарка по штампам: клиент за них не платит (price = 0), а полная цена остаётся.
Выгрузка в CSV
Любой список отдаётся файлом, если добавить format=csv. Те же поля, что в JSON, только плоские — вложенные значения вроде остатков по точкам в файл не попадают.
curl "https://api.elixtime.ru/v1/api/clients?format=csv&limit=1000" \ -H "X-Api-Key: elx_ваш_ключ" -o clients.csv
Файл приходит в UTF-8 с BOM — Excel в Windows откроет кириллицу без бубна. Поддерживают выгрузку: /clients, /bookings,/products, /stock-movements, /reviews,/gift-certificates, /abonements.
Для полной выгрузки идите страницами по offset: ответ всегда содержит total, а limit ограничен 200–500 в зависимости от метода.
Ошибки
Тело ошибки всегда одной формы. Ветвитесь по code — он не меняется, в отличие от текста.
{ "error": { "code": "revoked_api_key", "message": "Ключ отозван." } }| HTTP | code | Когда |
|---|---|---|
401 | missing_api_key | Заголовок X-Api-Key не передан |
401 | malformed_api_key | Ключ не начинается с elx_ |
401 | invalid_api_key | Такого ключа не существует |
401 | revoked_api_key | Ключ отозван в настройках салона |
403 | read_only_key | Ключу с правом read запрещена запись |
404 | booking_not_found | Записи нет — или она принадлежит другому салону |
409 | booking_completed | Завершённый визит нельзя отменить |
422 | client_contact_required | Не передан ни client_phone, ни client_email |
Методы
/pingreadПроверка ключа
Самый дешёвый запрос: подтверждает, что ключ рабочий, и сообщает, какому салону он принадлежит. Удобно вызывать сразу после того, как пользователь вставил ключ в вашу форму.
{
"ok": true,
"business": {
"id": "62312670-1ddf-46c1-89d8-98377d3e77cf",
"name": "Барбершоп Сезон",
"slug": "barbershop-sezon",
"timezone": "Europe/Moscow"
},
"scopes": "read"
}/servicesreadУслуги
Прайс салона: длительность в минутах, цена в рублях, категория.
| Параметр | Описание |
|---|---|
limit | до 500, по умолчанию 100 |
offset | смещение для постраничного обхода |
{
"items": [
{
"id": "1e2bd6a5-…",
"name": "Стрижка",
"category": "Услуги",
"duration_min": 60,
"price": 1200,
"active": true
}
],
"total": 1, "limit": 100, "offset": 0
}/employeesreadМастера
Сотрудники салона. Неактивные не удаляются, а помечаются active: false — записи за ними сохраняются.
| Параметр | Описание |
|---|---|
limit | до 500 |
offset | — |
{
"items": [
{ "id": "66d53ed4-…", "name": "Алексей", "role": null, "phone": null, "active": true }
],
"total": 1, "limit": 100, "offset": 0
}/locationsreadТочки
Адреса салона. Отдаются целиком, без пагинации — их всегда немного.
{
"items": [
{ "id": "72b0c9c4-…", "name": "Центр", "address": "Краснодар, ул. Красная, 1" }
],
"total": 1, "limit": 1, "offset": 0
}/clientsreadКлиенты
База клиентов салона. Поиск идёт одновременно по имени, телефону и почте.
| Параметр | Описание |
|---|---|
search | подстрока в имени, телефоне или email |
format | json (по умолчанию) или csv |
limit | до 200, по умолчанию 50 |
offset | — |
{
"items": [
{
"id": "f041b05b-…",
"name": "Мария Реброва",
"phone": "+79182001406",
"email": null,
"created_at": "2026-07-09T15:19:10.361026+00:00"
}
],
"total": 1, "limit": 50, "offset": 0
}/bookingsreadЗаписи
Записи за период. Без date_from и date_to отдаются ближайшие 30 дней — чтобы случайный запрос не выгреб всю историю салона.
| Параметр | Описание |
|---|---|
date_from | ISO 8601, включительно |
date_to | ISO 8601, исключительно |
status | pending · confirmed · completed · cancelled · no_show |
employee_id | записи одного мастера |
format | json (по умолчанию) или csv |
limit | до 200, по умолчанию 50 |
offset | — |
{
"items": [
{
"id": "9f1c…",
"status": "confirmed",
"start": "2026-08-20T10:00:00+03:00",
"end": "2026-08-20T11:00:00+03:00",
"employee_id": "66d53ed4-…",
"service_id": "1e2bd6a5-…",
"location_id": "72b0c9c4-…",
"client_id": "f041b05b-…",
"client_name": "Мария Реброва",
"price": 1200,
"full_price": 1200,
"paid_amount_kopecks": 60000,
"refunded_amount_kopecks": 0,
"payment_status": "succeeded",
"comment": null,
"created_at": "2026-08-18T09:12:44.100Z"
}
],
"total": 1, "limit": 50, "offset": 0
}/bookings/{id}readОдна запись
Та же структура, что и в списке. Чужая запись отдаёт 404, а не 403 — существование чужих данных не подтверждается.
{ "id": "9f1c…", "status": "confirmed", "start": "…", "…": "…" }/productsreadТовары и остатки
Расходники со складскими остатками. Остаток лежит отдельной строкой на каждую пару товар-точка, поэтому здесь он уже собран в stock — второй запрос на каждый товар не нужен.
| Параметр | Описание |
|---|---|
location_id | остатки одной точки |
low_stock | true — только то, что на минимуме или ниже |
format | json (по умолчанию) или csv |
limit | до 500 |
offset | — |
{
"items": [
{
"id": "b41f…",
"name": "Краска 6.0",
"sku": "KR-60",
"unit": "мл",
"cost_per_unit": 4,
"min_stock": 500,
"active": true,
"qty_total": 1250,
"stock": [
{ "location_id": "72b0c9c4-…", "qty_left": 1250, "updated_at": "2026-08-19T18:04:11Z" }
]
}
],
"total": 1, "limit": 100, "offset": 0
}/stock-movementsreadДвижения склада
Приход, списание и корректировки. Без дат — последние 30 дней. qty_delta со знаком: минус — списание (в том числе автоматическое после визита, тогда заполнен booking_id), плюс — приход.
| Параметр | Описание |
|---|---|
date_from / date_to | ISO 8601 |
product_id | по одному товару |
format | json или csv |
limit | до 500 |
offset | — |
{
"items": [
{
"id": "7c2a…",
"product_id": "b41f…",
"location_id": "72b0c9c4-…",
"qty_delta": -60,
"kind": "consumption",
"booking_id": "9f1c…",
"note": null,
"created_at": "2026-08-19T11:32:07Z"
}
],
"total": 1, "limit": 100, "offset": 0
}/reviewsreadОтзывы
Оценки и тексты отзывов вместе со ссылками на визит, мастера и услугу.
| Параметр | Описание |
|---|---|
status | pending · published · hidden |
format | json или csv |
limit | до 200 |
offset | — |
{
"items": [
{
"id": "3ad9…",
"rating": 5,
"comment": "Всё понравилось",
"author_name": "Мария",
"status": "published",
"booking_id": "9f1c…",
"employee_id": "66d53ed4-…",
"service_id": "1e2bd6a5-…",
"created_at": "2026-08-18T14:20:01Z"
}
],
"total": 1, "limit": 50, "offset": 0
}/gift-certificatesreadСертификаты
Номиналы и остатки в рублях. Код отдаётся целиком — ключ уже подтверждает, что запрос от самого салона.
| Параметр | Описание |
|---|---|
status | pending (не оплачен) · active · used_up · expired · cancelled |
format | json или csv |
limit | до 200 |
offset | — |
{
"items": [
{
"id": "cd10…",
"code": "GIFT-4K7M2P",
"status": "active",
"initial_amount": 5000,
"balance": 2600,
"buyer_name": "Ольга",
"recipient_name": "Мария",
"expires_at": "2027-02-19T00:00:00Z",
"created_at": "2026-08-19T09:00:00Z"
}
],
"total": 1, "limit": 50, "offset": 0
}/abonementsreadАбонементы
Проданные абонементы клиентов: сколько посещений осталось и до какой даты.
| Параметр | Описание |
|---|---|
client_id | по одному клиенту |
status | active · exhausted (посещения кончились) · expired |
format | json или csv |
limit | до 200 |
offset | — |
{
"items": [
{
"id": "aa31…",
"client_id": "f041b05b-…",
"template_id": "77c2…",
"template_name": "5 маникюров",
"uses_left": 3,
"price_paid": 9000,
"status": "active",
"sold_at": "2026-07-30T12:00:00Z",
"expires_at": "2026-10-28T00:00:00Z"
}
],
"total": 1, "limit": 50, "offset": 0
}/bookingsread_writeСоздать запись
Создаёт визит теми же правилами, что и виджет: занятый слот получает отказ на уровне ограничения в базе, действуют лимиты тарифа, уходят уведомления клиенту и мастеру. Клиент находится по телефону или почте, а если такого ещё нет — заводится.
| Параметр | Описание |
|---|---|
employee_id | обязательно |
service_id | обязательно |
start | ISO 8601, обязательно |
client_name | обязательно |
client_phone / client_email | нужен хотя бы один |
location_id | если у салона несколько точек |
comment | до 1000 символов |
// 201 Created — тело как у GET /bookings/{id}
{ "id": "…", "status": "pending", "start": "2026-08-21T12:00:00+03:00", "…": "…" }/bookings/{id}/cancelread_writeОтменить запись
Переводит визит в cancelled теми же правилами, что и отмена в админке: визит возвращается на абонемент, номинал — на сертификат, списанные баллы — клиенту, оплата возвращается через платёжный шлюз (по Робокассе визит помечается «вернуть вручную»), клиент получает уведомление, очередь ожидания — предложение занять слот. Повторный вызов безопасен — уже отменённая запись просто возвращается как есть. Завершённый визит отменить нельзя.
| Параметр | Описание |
|---|---|
reason | необязательно, дописывается в комментарий |
{ "id": "9f1c…", "status": "cancelled", "…": "…" }Пример: занять слот из своей формы
curl -X POST https://api.elixtime.ru/v1/api/bookings \
-H "X-Api-Key: elx_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{
"employee_id": "66d53ed4-…",
"service_id": "1e2bd6a5-…",
"start": "2026-08-21T12:00:00+03:00",
"client_name": "Мария Реброва",
"client_phone": "+79182001406"
}'Если мастер в это время занят, запрос вернёт ошибку — проверка стоит ограничением в базе данных, а не в коде, поэтому две одновременные попытки занять один слот не пройдут обе.
Вебхуки
Обратное направление: ElixTime сам постучится к вам, когда что-то произошло. Настраиваются там же, в Настройки → Интеграции, отдельно от ключей — у каждого вебхука свой секрет.
| Событие | Когда приходит |
|---|---|
booking.created | Клиент или администратор создал запись |
booking.confirmed | Запись подтверждена |
booking.cancelled | Запись отменена |
booking.completed | Визит завершён |
booking.rescheduled | Изменилось время или мастер |
payment.succeeded | ЮKassa подтвердила оплату |
tip.received | Получены чаевые |
Заголовки доставки
X-ElixTime-Event: booking.created X-ElixTime-Signature: 9f2c… (HMAC-SHA256 от тела, hex) X-ElixTime-Timestamp: 2026-08-20T09:14:03.918Z X-ElixTime-Delivery: 4be1… (id доставки, для дедупликации) X-ElixTime-Attempt: 1
Проверка подписи
Считайте HMAC-SHA256 от сырого тела запроса секретом вебхука и сравните с заголовком. Сравнивайте константным по времени сравнением, а не обычным ==.
import hmac, hashlib
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)const crypto = require('crypto');
function verify(rawBody, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}Доставка идёт с повторами и бэк-оффом, поэтому одно событие может прийти дважды. Дедуплицируйте по X-ElixTime-Delivery и отвечайте 2xx как можно быстрее — обработку выносите в очередь.
Ограничения
- До 10 активных ключей на салон. Отозванные не считаются.
limit: до 500 для справочников, до 200 для клиентов и записей.- Публичный API входит в тариф Pro и выше.
- Версия в адресе:
/v1/. Ломающие изменения выйдут как/v2/, старая версия продолжит работать.
Не хватает метода — напишите, что вы строите: support@elxcorp.com.