Referência da API do Walaora
Conecte um PDV, uma loja virtual ou um sistema interno. JSON via HTTPS, uma chave de API por integração.
Autenticação
Crie uma chave em Painel → API e webhooks (plano Empresarial ou Corporativo). Envie-a em todas as requisições. As chaves são exibidas uma única vez; revogue-as se forem expostas. Limite: 120 requisições por minuto por chave.
Authorization: Bearer wl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxEndpoints
Base URL: https://walaora.com/api/v1
| GET | /programsLista os programas de fidelidade. |
| GET | /customers?phone=0551234567&tag=vipBusca clientes (50 por página, ?page=2…). ?tag=vip lista os clientes com essa etiqueta; cada cliente inclui suas etiquetas. |
| POST | /customersCadastra um cliente em um programa. Corpo: { programId, name, phone, email?, marketingConsent?, locale? }. Retorna 201 quando criado e 200 se o cliente já estiver cadastrado. |
| GET | /cards/{serial}Cartão pelo código no cartão / QR do cliente. |
| POST | /cards/{serial}/earnProgramas de carimbos: { stamps?: 1–10 }. Programas de pontos: { amount } na moeda do espaço de trabalho. Opcional: { branchId }. Cartões multisselos: envie setId, uma das séries do cartão. |
| POST | /cards/{serial}/redeemResgata uma recompensa disponível, ou envie {"rewardId"} para resgatar uma recompensa do catálogo com preço em carimbos/pontos. |
| POST | /cards/{serial}/sellPacotes pré-pagos: vende um pacote e soma seus usos ao cartão (o pagamento é feito no seu caixa). Webhook: package.sold. |
| POST | /cards/{serial}/usePacotes pré-pagos: desconta um uso (recusado se vazio ou expirado). Cartões de funcionário: registra o benefício dentro do limite diário. Webhooks: package.used, perk.used. |
| GET | /coupons/{code}Verifica um cupom sem usá-lo: sua oferta, se pode ser usado e, se não, por quê (resgatado, expirado ou pausado). |
| POST | /coupons/{code}/redeemUsa um cupom uma vez. Opcional: { amount } (a venda, na moeda do espaço de trabalho) e { branchId }. 409 quando já foi usado ou a oferta foi encerrada. Webhook: coupon.redeemed. |
| GET | /coupon-offersLista as ofertas de cupom com valor, data de término e se é possível criar códigos agora (usable). |
| POST | /coupon-offers/{id}/couponsCria códigos de uma oferta. { customerId } dá um código a esse cliente (aparece na página do cartão dele); ou { count: 1–500 } códigos sem cliente para imprimir ou compartilhar. 201 com os códigos; 409 se a oferta estiver pausada ou tiver 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}'Novas tentativas e idempotência
Em requisições POST, envie um cabeçalho Idempotency-Key (qualquer texto único, por exemplo o número do seu cupom). Repetir a requisição com a mesma chave em até 24 horas retorna a resposta original, em vez de carimbar duas vezes.
Erros
Os erros retornam um corpo JSON { error: { code, message } } com um status HTTP: 401 chave inválida · 403 plan_required · 404 não encontrado · 409 cooldown / no_reward / program_inactive · 422 validação · 429 limite de requisições excedido.
Webhooks
Adicione um endpoint HTTPS no painel e escolha os eventos. Cada entrega é um POST com JSON { id, type, createdAt, data } e estes cabeçalhos: Walaora-Event, Walaora-Delivery e Walaora-Signature. Responda com 2xx em até 10 segundos. Entregas com falha são reenviadas após 1 min, 5 min, 30 min, 2 h e 12 h.
Eventos
customer.enrolledcard.earnedreward.unlockedreward.redeemedcard.adjustedcustomer.deletedfeedback.receivedpackage.soldpackage.usedperk.usedcoupon.redeemed
Verificação de assinaturas (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 ?? ""));
}