Skip to content
walaoraWalaora

Walaora API reference

Connect a POS, e-commerce store or internal system. JSON over HTTPS, one API key per integration.

Only send customers’ data with their knowledge, and set marketingConsent to true only when the customer explicitly agreed.

Authentication

Create a key in Dashboard → API & webhooks (Business or Enterprise plan). Send it on every request. Keys are shown once; revoke them if exposed. Limit: 120 requests per minute per key.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpoints

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

GET/programs

List loyalty programs.

GET/customers?phone=0551234567&tag=vip

Find customers (50 per page, ?page=2…). ?tag=vip lists customers with that tag; each customer includes its tags.

POST/customers

Enroll a customer in a program. Body: { programId, name, phone, email?, marketingConsent?, locale? }. Returns 201 when created, 200 if already enrolled.

GET/cards/{serial}

Card by the code on the customer’s card / QR.

POST/cards/{serial}/earn

Stamp programs: { stamps?: 1–10 }. Points programs: { amount } in the workspace currency. Optional { branchId }. Multi-stamp cards: send setId, one of the card’s sets.

POST/cards/{serial}/redeem

Redeem one available reward, or send {"rewardId"} to redeem a catalogue reward priced in stamps/points.

POST/cards/{serial}/sell

Prepaid packages: sell one package; its uses are added to the card (payment is taken at your till). Webhook: package.sold.

POST/cards/{serial}/use

Prepaid packages: take one use (refused when empty or expired). Employee cards: record the perk within its daily limit. Webhooks: package.used, perk.used.

GET/coupons/{code}

Check a coupon without using it: its offer, whether it can be used and, if not, why (redeemed, expired or paused).

POST/coupons/{code}/redeem

Use a coupon once. Optional { amount } (the sale, in the workspace currency) and { branchId }. 409 when already used or the offer has ended. Webhook: coupon.redeemed.

GET/coupon-offers

List coupon offers with their value, end date and whether codes can be created now (usable).

POST/coupon-offers/{id}/coupons

Create codes of an offer. { customerId } gives one code to that customer (it shows on their card page); or { count: 1–500 } unassigned codes to print or share. 201 with the codes; 409 when the offer is paused or has ended.

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

Retries & idempotency

For POST requests, send an Idempotency-Key header (any unique string, e.g. your receipt number). Retrying with the same key within 24 hours returns the original response instead of stamping twice.

Errors

Errors return a JSON body { error: { code, message } } with an HTTP status: 401 bad key · 403 plan_required · 404 not found · 409 cooldown / no_reward / program_inactive · 422 validation · 429 rate limited.

Webhooks

Add an HTTPS endpoint in the dashboard and choose events. Each delivery is a POST with JSON { id, type, createdAt, data } and these headers: Walaora-Event, Walaora-Delivery and Walaora-Signature. Respond with 2xx within 10 seconds. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h.

Events

Verifying signatures (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 ?? ""));
}