Referencia de la API de Walaora
Conecte un TPV, una tienda en línea o un sistema interno. JSON sobre HTTPS, una clave de API por integración.
Autenticación
Cree una clave en Panel → API y webhooks (plan Negocios o Empresas). Envíela en cada solicitud. Las claves se muestran una sola vez; revóquelas si quedan expuestas. Límite: 120 solicitudes por minuto por clave.
Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxEndpoints
Base URL: https://walaora.com/api/v1
| GET | /programsListar los programas de fidelidad. |
| GET | /customers?phone=0551234567&tag=vipBuscar clientes (50 por página, ?page=2…). ?tag=vip lista los clientes con esa etiqueta; cada cliente incluye sus etiquetas. |
| POST | /customersInscribir a un cliente en un programa. Cuerpo: { programId, name, phone, email?, marketingConsent?, locale? }. Devuelve 201 si se crea y 200 si ya estaba inscrito. |
| GET | /cards/{serial}Tarjeta según el código de la tarjeta / QR del cliente. |
| POST | /cards/{serial}/earnProgramas de sellos: { stamps?: 1–10 }. Programas de puntos: { amount } en la moneda del espacio de trabajo. Opcional: { branchId }. Tarjetas de sellos múltiples: envía setId, una de las series de la tarjeta. |
| POST | /cards/{serial}/redeemCanjear una recompensa disponible, o enviar {"rewardId"} para canjear una recompensa del catálogo con precio en sellos/puntos. |
| POST | /cards/{serial}/sellPaquetes prepago: vende un paquete y suma sus usos a la tarjeta (el pago se cobra en su caja). Webhook: package.sold. |
| POST | /cards/{serial}/usePaquetes prepago: descuenta un uso (rechazado si no quedan o caducó). Tarjetas de empleado: registra la ventaja dentro de su límite diario. Webhooks: package.used, perk.used. |
| GET | /coupons/{code}Comprobar un cupón sin usarlo: su oferta, si se puede usar y, si no, por qué (canjeado, caducado o en pausa). |
| POST | /coupons/{code}/redeemUsar un cupón una vez. Opcional: { amount } (la venta, en la moneda del espacio de trabajo) y { branchId }. 409 si ya se usó o la oferta ha finalizado. Webhook: coupon.redeemed. |
| GET | /coupon-offersLista las ofertas de cupones con su valor, fecha de fin y si ahora se pueden crear códigos (usable). |
| POST | /coupon-offers/{id}/couponsCrea códigos de una oferta. { customerId } da un código a ese cliente (aparece en la página de su tarjeta); o { count: 1–500 } códigos sin asignar para imprimir o compartir. 201 con los códigos; 409 si la oferta está en pausa o ha terminado. |
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}'Reintentos e idempotencia
En las solicitudes POST, envíe un encabezado Idempotency-Key (cualquier cadena única, p. ej., su número de recibo). Si reintenta con la misma clave en un plazo de 24 horas, recibirá la respuesta original en lugar de sellar dos veces.
Errores
Los errores devuelven un cuerpo JSON { error: { code, message } } con un estado HTTP: 401 clave incorrecta · 403 plan_required · 404 no encontrado · 409 cooldown / no_reward / program_inactive · 422 validación · 429 límite de solicitudes superado.
Webhooks
Añada un endpoint HTTPS en el panel y elija los eventos. Cada entrega es un POST con JSON { id, type, createdAt, data } y estos encabezados: Walaora-Event, Walaora-Delivery y Walaora-Signature. Responda con un 2xx en un máximo de 10 segundos. Las entregas fallidas se reintentan después de 1 min, 5 min, 30 min, 2 h y 12 h.
Eventos
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
Verificación de firmas (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 ?? ""));
}