Довідник API Walaora
Підключіть POS-систему, інтернет-магазин або внутрішню систему. JSON через HTTPS, один ключ API на кожну інтеграцію.
Автентифікація
Створіть ключ у розділі Панель керування → API і вебхуки (тариф «Бізнес» або «Корпоративний»). Надсилайте його з кожним запитом. Ключ показується лише один раз; якщо він потрапив до сторонніх осіб, відкличте його. Ліміт: 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 ?? ""));
}