Справочник API Walaora
Подключите кассовую систему, интернет-магазин или внутреннюю систему. JSON через HTTPS, отдельный ключ API для каждой интеграции.
Аутентификация
Создайте ключ в разделе Панель управления → API и Webhooks (тариф «Бизнес» или «Корпоративный»). Передавайте его в каждом запросе. Ключ показывается только один раз; если он стал известен посторонним, отзовите его. Лимит: 120 запросов в минуту на ключ.
Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxЭндпоинты
Base URL: https://walaora.com/api/v1
| GET | /programsСписок программ лояльности. |
| GET | /customers?phone=0551234567&tag=vipПоиск клиентов (50 на страницу, ?page=2…). ?tag=vip выводит клиентов с этой меткой; у каждого клиента есть его метки. |
| POST | /customersРегистрация клиента в программе. Тело: { programId, name, phone, email?, marketingConsent?, locale? }. Возвращает 201 при создании и 200, если клиент уже зарегистрирован. |
| GET | /cards/{serial}Карта по коду на карте клиента / QR. |
| POST | /cards/{serial}/earnПрограммы со штампами: { stamps?: 1–10 }. Программы с баллами: { amount } в валюте рабочего пространства. Необязательно: { branchId }. Карты с несколькими штампами: передайте setId — один из наборов карты. |
| POST | /cards/{serial}/redeemПолучение одной доступной награды или отправьте {"rewardId"}, чтобы обменять награду из каталога, оцениваемую в штампах/баллах. |
| POST | /cards/{serial}/sellПредоплаченные пакеты: продаёт пакет, его использования добавляются на карту (оплата на вашей кассе). Webhook: package.sold. |
| POST | /cards/{serial}/useПредоплаченные пакеты: списывает одно использование (отказ, если пусто или истёк срок). Карты сотрудников: отмечает привилегию в пределах дневного лимита. Webhooks: package.used, perk.used. |
| GET | /coupons/{code}Проверка купона без его использования: его акция, можно ли его использовать и, если нет, почему (уже использован, истёк срок или приостановлен). |
| POST | /coupons/{code}/redeemОднократное использование купона. Необязательно: { amount } (сумма покупки в валюте рабочего пространства) и { branchId }. 409, если купон уже использован или акция завершена. Webhook: coupon.redeemed. |
| GET | /coupon-offersСписок купонных акций: значение, дата окончания и можно ли сейчас создавать коды (usable). |
| POST | /coupon-offers/{id}/couponsСоздаёт коды акции. { customerId } выдаёт этому клиенту один код (он появится на странице его карты); или { count: 1–500 } кодов без привязки к клиенту для печати или раздачи. 201 с кодами; 409, если акция приостановлена или закончилась. |
curl -X POST https://walaora.com/api/v1/cards/ABCDE23456/earn \
-H "Authorization: Bearer wl_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: receipt-10045" \
-d '{"stamps": 1}'Повторные запросы и идемпотентность
В POST-запросах передавайте заголовок Idempotency-Key (любая уникальная строка, например номер чека). Повторный запрос с тем же ключом в течение 24 часов вернёт исходный ответ, а не начислит штамп дважды.
Ошибки
Ошибки возвращаются в теле JSON { error: { code, message } } с HTTP-статусом: 401 неверный ключ · 403 plan_required · 404 не найдено · 409 cooldown / no_reward / program_inactive · 422 ошибка валидации · 429 превышен лимит запросов.
Webhooks
Добавьте HTTPS-эндпоинт в панели управления и выберите события. Каждая доставка — это POST-запрос с JSON { id, type, createdAt, data } и заголовками Walaora-Event, Walaora-Delivery и Walaora-Signature. Ответьте кодом 2xx в течение 10 секунд. Неудачные доставки повторяются через 1 мин, 5 мин, 30 мин, 2 ч и 12 ч.
События
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
Проверка подписей (Node.js)
import crypto from "node:crypto";
// rawBody: the exact request body string; header: req.headers["walaora-signature"]
export function verifyWalaora(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // replay window
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}