Перейти к содержимому
walaoraWalaora

Справочник API Walaora

Подключите кассовую систему, интернет-магазин или внутреннюю систему. JSON через HTTPS, отдельный ключ API для каждой интеграции.

Передавайте данные клиентов только с их ведома и устанавливайте marketingConsent в true, только если клиент явно дал согласие.

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

Создайте ключ в разделе Панель управления → 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 ч.

События

Проверка подписей (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 ?? ""));
}