Przejdź do treści
walaoraWalaora

Dokumentacja API Walaora

Połącz system POS, sklep internetowy lub system wewnętrzny. JSON przez HTTPS, jeden klucz API na integrację.

Przesyłaj dane klientów wyłącznie za ich wiedzą, a marketingConsent ustawiaj na true tylko wtedy, gdy klient wyraźnie się zgodził.

Uwierzytelnianie

Utwórz klucz w Panel → API i webhooki (plan Biznes lub Enterprise). Wysyłaj go z każdym żądaniem. Klucze są wyświetlane tylko raz; jeśli wyciekną, unieważnij je. Limit: 120 żądań na minutę na klucz.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpointy

Base URL: https://walaora.com/api/v1

GET/programs

Lista programów lojalnościowych.

GET/customers?phone=0551234567&tag=vip

Wyszukiwanie klientów (50 na stronę, ?page=2…). ?tag=vip zwraca klientów z tym tagiem; każdy klient zawiera swoje tagi.

POST/customers

Zapisanie klienta do programu. Treść: { programId, name, phone, email?, marketingConsent?, locale? }. Zwraca 201 po utworzeniu, 200 jeśli klient jest już zapisany.

GET/cards/{serial}

Karta według kodu z karty klienta / kodu QR.

POST/cards/{serial}/earn

Programy pieczątkowe: { stamps?: 1–10 }. Programy punktowe: { amount } w walucie przestrzeni roboczej. Opcjonalnie { branchId }. Karty wielu pieczątek: wyślij setId — jeden z zestawów karty.

POST/cards/{serial}/redeem

Realizacja jednej dostępnej nagrody lub wyślij {"rewardId"}, aby zrealizować nagrodę z katalogu wycenioną w pieczątkach/punktach.

POST/cards/{serial}/sell

Pakiety przedpłacone: sprzedaje pakiet, którego użycia trafiają na kartę (płatność przy Twojej kasie). Webhook: package.sold.

POST/cards/{serial}/use

Pakiety przedpłacone: odejmuje jedno użycie (odmowa, gdy brak lub wygasł). Karty pracownika: zapisuje przywilej w dziennym limicie. Webhooki: package.used, perk.used.

GET/coupons/{code}

Sprawdzenie kuponu bez jego użycia: jego oferta, czy można go użyć, a jeśli nie — dlaczego (wykorzystany, wygasły lub wstrzymany).

POST/coupons/{code}/redeem

Jednorazowe użycie kuponu. Opcjonalnie { amount } (kwota sprzedaży w walucie przestrzeni roboczej) i { branchId }. 409, gdy kupon został już wykorzystany lub oferta się zakończyła. Webhook: coupon.redeemed.

GET/coupon-offers

Lista ofert kuponowych z wartością, datą końca i informacją, czy można teraz tworzyć kody (usable).

POST/coupon-offers/{id}/coupons

Tworzy kody oferty. { customerId } daje temu klientowi jeden kod (widoczny na stronie jego karty); albo { count: 1–500 } nieprzypisanych kodów do wydruku lub udostępnienia. 201 z kodami; 409, gdy oferta jest wstrzymana lub zakończona.

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}'

Ponawianie i idempotentność

W żądaniach POST wysyłaj nagłówek Idempotency-Key (dowolny unikalny ciąg, np. numer paragonu). Ponowienie z tym samym kluczem w ciągu 24 godzin zwraca pierwotną odpowiedź zamiast dodawać pieczątkę drugi raz.

Błędy

Błędy zwracają treść JSON { error: { code, message } } ze statusem HTTP: 401 nieprawidłowy klucz · 403 plan_required · 404 nie znaleziono · 409 cooldown / no_reward / program_inactive · 422 błąd walidacji · 429 przekroczony limit żądań.

Webhooki

Dodaj endpoint HTTPS w panelu i wybierz zdarzenia. Każde doręczenie to żądanie POST z JSON { id, type, createdAt, data } i nagłówkami: Walaora-Event, Walaora-Delivery oraz Walaora-Signature. Odpowiedz kodem 2xx w ciągu 10 sekund. Nieudane doręczenia są ponawiane po 1 min, 5 min, 30 min, 2 h i 12 h.

Zdarzenia

Weryfikacja podpisów (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 ?? ""));
}