Fidelización
Disponible desde 1.25.0. Las respuestas llevan Cache-Control: private, no-store. Si el programa está desactivado o la tienda está en modo coexistencia, las rutas responden 409 loyalty_unavailable. Consulta la guía de fidelización.
Rutas del cliente (sesión iniciada)
El cliente debe tener una cuenta de puntos con el correo verificado. Si no lo tiene, las rutas responden 400 loyalty_invalid.
| Método | Ruta | Parámetros | Respuesta |
|---|---|---|---|
| GET | /loyalty/me | page (movimientos, 25 por página) | { account, ledger{items[], page, total}, rewards[] } |
| POST | /loyalty/me/redeem | reward_id (por defecto points), points, idempotency (obligatorio) | { coupon_code, points, balance } |
| POST | /loyalty/me/return | coupon_code | Movimiento de devolución de puntos. |
| PUT | /loyalty/me/birthday | birthday (AAAA-MM-DD) | { saved: true } |
| POST | /loyalty/me/verify | — | { sent: true }: envía un enlace de verificación por correo. |
redeemcrea un cupón de un solo uso (prefijonw-) restringido al correo del cliente. No lo aplica al carrito: la tienda lo hace en el checkout.- La fecha de cumpleaños no puede ser futura y solo se puede cambiar una vez al año.
Ruta pública
GET /loyalty/preview devuelve lo que la tienda muestra en el carrito: { enabled, program_name, preview, balance, verified, logged_in, min_points, rewards[], account_url, login_url, cart_message, ... }. preview son los puntos que ganaría el carrito actual. Si el programa está apagado, responde { enabled: false }.
Rutas de gestión
| Método | Ruta | Uso |
|---|---|---|
| GET | /loyalty/accounts | Buscar cuentas: search (correo, teléfono o nombre), level, status (active o blocked), linked (1 o 0), page, per_page (25 por defecto, máximo 100). Responde { items, total, page, per_page } dentro de data. |
| GET | /loyalty/accounts/{id} | Detalle, movimientos y recompensas. |
| POST | /loyalty/accounts/{id}/adjust | Ajuste manual: points (positivo o negativo), note (obligatoria, hasta 255 caracteres) e idempotency. |
| POST | /loyalty/accounts/{id}/redeem | Canje en caja: order_id (pedido abierto del mismo correo), reward_id, points e idempotency. Aplica el cupón al pedido y recalcula el total. |
| POST | /loyalty/accounts/{id}/return | Devolver los puntos de un cupón: coupon_code. |
| POST | /loyalty/accounts/{id}/verify | Sin code: envía un código de 6 dígitos al cliente. Con code: verifica la cuenta. |
| PUT | /loyalty/accounts/{id}/status | status (active o blocked) y note (obligatoria). |
| POST | /loyalty/accounts/merge | Fusionar cuentas: source_id, target_id, note. |
| GET | /loyalty/stats | from y to (AAAA-MM-DD, hasta 366 días). Por defecto, desde el inicio del mes. |
| GET, PUT | /settings/loyalty | Ajustes del programa. |
| POST | /loyalty/import | Importar CSV (ver abajo). |
| GET | /loyalty/export | Exportar CSV por páginas de 100: { csv, done, page }. |
| POST | /loyalty/preview-rules | Simula cuántos puntos daría un evento: settings, context, event. |
| POST | /loyalty/apply-expiry | Programa el vencimiento de puntos en segundo plano. |
Canje en caja
curl -X POST https://tienda.example.test/wp-json/notiwoo/v1/loyalty/accounts/58/redeem \
-H "Authorization: Bearer <token>" \
-H "X-Notiwoo-Actor: notifier:15" \
-H "Content-Type: application/json" \
-d '{"order_id": 1240, "reward_id": "points", "points": 500, "idempotency": "caja-1240-0001"}'
{ "success": true, "data": { "coupon_code": "nw-k3j9x0q2m5v8b1n4c7z6l0p9w2e5", "points": 500, "balance": 1320, "order_total": "41000.00" } }
La cabecera X-Notiwoo-Actor es opcional y registra quién hizo el canje en el historial.
Verificación por código
El código de 6 dígitos vale 15 minutos y admite 5 intentos. Solo se puede enviar uno por minuto por cuenta; si pides otro antes, la respuesta incluye retry_after: 60.
Importar CSV
POST /loyalty/import con csv (texto de hasta 2 MB y 5000 filas) y dry_run.
| Columna | Alias |
|---|---|
email | user_email, customer_email |
points | points_balance, balance |
name | first_name + last_name |
phone, referral_code | — |
Por defecto dry_run es true: valida y responde { valid, count, points, errors[], preview[] } sin importar nada. Envía dry_run: false para importar.
Objetos
Cuenta: id, email, phone (formato internacional), name, user_id, email_verified_at, referral_code, level_id, birthday, balance, lifetime_earned, status, whatsapp_consent, created_at, updated_at.
Movimiento: id, type (earn, redeem, adjust, expire, restore o revert), points, remaining, expires_at, source, campaign_id, order_id, reward_id, coupon_code, note, actor, created_at.
Ajustes del programa
GET /settings/loyalty responde { settings, log[], whatsapp_ready, currency }. log es el historial de cambios de los ajustes.
PUT /settings/loyalty recibe el objeto completo de ajustes. Si cambias el valor del punto o el modo de identificación, debes confirmarlo con confirm_point_value: true o confirm_identity_risk: true.
Grupos principales de settings:
| Grupo | Contenido |
|---|---|
enabled, program_name | Activación y nombre. |
identity | Cómo se identifica al cliente: email, account o phone. |
earn | Cuándo se ganan los puntos (completed o finalized), importe por punto, redondeo e inclusión del domicilio. |
redeem | Valor del punto, mínimo de puntos, porcentaje máximo del pedido y vigencia del cupón. |
expiry | Vencimiento y aviso previo. |
level_settings, levels[] | Niveles. |
campaigns[] | Campañas: purchase, first_order, signup, birthday, review, referral, multiplier y level_up. |
rewards[] | Recompensas: points_discount, fixed_discount, percent_discount, free_delivery y free_product. |
referral | Referidos. |
notify | Canal de cada aviso: none, email, whatsapp o both. |
Si la validación falla, responde 400 loyalty_settings con las rutas de los campos a revisar.
Errores
| Código | HTTP | Causa |
|---|---|---|
loyalty_invalid | 400 | Puntos insuficientes o por debajo del mínimo, porcentaje máximo superado, recompensa no disponible, cuenta bloqueada o inexistente, código incorrecto, clave de idempotencia no válida, CSV con errores… |
loyalty_settings | 400 | Ajustes no válidos. |
loyalty_unavailable | 409 | Programa desactivado, operación simultánea sobre la misma cuenta o WhatsApp sin configurar. |