Zum Inhalt springen
walaoraWalaora

Walaora API-Referenz

Verbinden Sie ein Kassensystem, einen Onlineshop oder ein internes System. JSON über HTTPS, ein API-Schlüssel pro Integration.

Übermitteln Sie Kundendaten nur mit dem Wissen der Kunden, und setzen Sie marketingConsent nur dann auf true, wenn der Kunde ausdrücklich zugestimmt hat.

Authentifizierung

Erstellen Sie einen Schlüssel unter Dashboard → API & Webhooks (Tarif Business oder Enterprise). Senden Sie ihn bei jeder Anfrage mit. Schlüssel werden nur einmal angezeigt; widerrufen Sie sie, falls sie offengelegt wurden. Limit: 120 Anfragen pro Minute und Schlüssel.

Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpunkte

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

GET/programs

Treueprogramme auflisten.

GET/customers?phone=0551234567&tag=vip

Kunden suchen (50 pro Seite, ?page=2…). ?tag=vip listet Kunden mit diesem Schlagwort; jeder Kunde enthält seine Schlagwörter.

POST/customers

Einen Kunden in einem Programm registrieren. Body: { programId, name, phone, email?, marketingConsent?, locale? }. Liefert 201 bei Neuanlage, 200 wenn bereits registriert.

GET/cards/{serial}

Karte anhand des Codes auf der Kundenkarte / dem QR-Code.

POST/cards/{serial}/earn

Stempelprogramme: { stamps?: 1–10 }. Punkteprogramme: { amount } in der Währung des Arbeitsbereichs. Optional { branchId }. Mehrfach-Stempelkarten: setId senden, eine der Reihen der Karte.

POST/cards/{serial}/redeem

Eine verfügbare Prämie einlösen oder {"rewardId"} senden, um eine Katalogprämie einzulösen, die in Stempeln/Punkten bepreist ist.

POST/cards/{serial}/sell

Prepaid-Pakete: verkauft ein Paket, dessen Nutzungen der Karte gutgeschrieben werden (Bezahlung an Ihrer Kasse). Webhook: package.sold.

POST/cards/{serial}/use

Prepaid-Pakete: zieht eine Nutzung ab (abgelehnt, wenn leer oder abgelaufen). Mitarbeiterkarten: erfasst den Vorteil im Tageslimit. Webhooks: package.used, perk.used.

GET/coupons/{code}

Einen Gutschein prüfen, ohne ihn einzulösen: sein Angebot, ob er einlösbar ist und, falls nicht, warum (eingelöst, abgelaufen oder pausiert).

POST/coupons/{code}/redeem

Einen Gutschein einmal einlösen. Optional { amount } (der Verkaufsbetrag in der Währung des Arbeitsbereichs) und { branchId }. 409, wenn bereits eingelöst oder das Angebot beendet ist. Webhook: coupon.redeemed.

GET/coupon-offers

Coupon-Angebote mit Wert, Enddatum und ob jetzt Codes erstellt werden können (usable).

POST/coupon-offers/{id}/coupons

Codes eines Angebots erstellen. { customerId } gibt diesem Kunden einen Code (er erscheint auf seiner Kartenseite); oder { count: 1–500 } nicht zugeordnete Codes zum Drucken oder Teilen. 201 mit den Codes; 409, wenn das Angebot pausiert oder abgelaufen ist.

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

Wiederholungen & Idempotenz

Senden Sie bei POST-Anfragen einen Idempotency-Key-Header (eine beliebige eindeutige Zeichenfolge, z. B. Ihre Belegnummer). Eine Wiederholung mit demselben Schlüssel innerhalb von 24 Stunden liefert die ursprüngliche Antwort, statt doppelt zu stempeln.

Fehler

Fehler liefern einen JSON-Body { error: { code, message } } mit einem HTTP-Status: 401 ungültiger Schlüssel · 403 plan_required · 404 nicht gefunden · 409 cooldown / no_reward / program_inactive · 422 Validierung · 429 Anfragelimit erreicht.

Webhooks

Fügen Sie im Dashboard einen HTTPS-Endpunkt hinzu und wählen Sie Ereignisse aus. Jede Zustellung ist ein POST mit JSON { id, type, createdAt, data } und diesen Headern: Walaora-Event, Walaora-Delivery und Walaora-Signature. Antworten Sie innerhalb von 10 Sekunden mit 2xx. Fehlgeschlagene Zustellungen werden nach 1 Min., 5 Min., 30 Min., 2 Std. und 12 Std. wiederholt.

Ereignisse

Signaturen prüfen (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 ?? ""));
}