Walaora API-Referenz
Verbinden Sie ein Kassensystem, einen Onlineshop oder ein internes System. JSON über HTTPS, ein API-Schlüssel pro Integration.
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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxEndpunkte
Base URL: https://walaora.com/api/v1
| GET | /programsTreueprogramme auflisten. |
| GET | /customers?phone=0551234567&tag=vipKunden suchen (50 pro Seite, ?page=2…). ?tag=vip listet Kunden mit diesem Schlagwort; jeder Kunde enthält seine Schlagwörter. |
| POST | /customersEinen 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}/earnStempelprogramme: { 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}/redeemEine verfügbare Prämie einlösen oder {"rewardId"} senden, um eine Katalogprämie einzulösen, die in Stempeln/Punkten bepreist ist. |
| POST | /cards/{serial}/sellPrepaid-Pakete: verkauft ein Paket, dessen Nutzungen der Karte gutgeschrieben werden (Bezahlung an Ihrer Kasse). Webhook: package.sold. |
| POST | /cards/{serial}/usePrepaid-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}/redeemEinen 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-offersCoupon-Angebote mit Wert, Enddatum und ob jetzt Codes erstellt werden können (usable). |
| POST | /coupon-offers/{id}/couponsCodes 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
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
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 ?? ""));
}