☰ Menú · Reglas y límites del local
Reglas y límites del local
Cómo controla un local lo que pueden hacer las cajas conectadas: niveles de cashback, categorías de productos, reservas, numeración de tickets, tickets enviados tarde, límites y escaneo de tarjetas.
Todo lo de esta página lo configura el propietario del local en Desarrolladores → Reglas para las cajas. Las reglas se aplican a todas las claves API del local; una clave concreta (una caja, un quiosco) puede tener sus propios valores para cualquiera de ellas. Tu integración lee las reglas con las que trabaja en GET /v1/account, dentro de settings.
"settings": {
"redemption": "hold",
"hold_minutes": 30,
"check_numbering": "per_register",
"offline_max_hours": 72,
"redeem_max_per_check": 50000,
"redeem_max_per_day": null,
"require_scan": true,
"scan_window_minutes": 30,
"allow_adjustments": false,
"adjustment_max": 50000
}Niveles de cashback
Un programa de cashback puede tener niveles: por ejemplo, Base 5 %, Plata 7 % desde la segunda visita, Oro 10 % a partir de 1000,00 gastados. Los niveles se cuentan por visitas, por importe gastado o por lo que llegue antes. El propietario los configura en el programa de fidelización. En la caja no hace falta nada: cada ticket genera cashback según el nivel del cliente después de ese ticket, y cada objeto member te dice en qué nivel está el cliente.
"cashback": {
"balance": 10200,
"held": 0,
"available": 10200,
"currency": "MDL",
"earn_rate": 7,
"level": "Silver",
"level_basis": "any",
"levels": [
{ "name": "Base", "earn_rate": 5, "min_visits": null, "min_spent": null },
{ "name": "Silver", "earn_rate": 7, "min_visits": 2, "min_spent": null },
{ "name": "Gold", "earn_rate": 10, "min_visits": null, "min_spent": 100000 }
],
"next_level": { "name": "Gold", "earn_rate": 10, "visits_left": null, "spent_left": 80000 },
...
}next_level.visits_left y next_level.spent_left están listos para mostrarlos al cliente ("2 visitas más para Plata"). display.receipt_lines ya incluye esa línea en el idioma del cliente.
Categorías de productos
Sube tus productos con su categoría una vez y después envía los cambios. Las categorías aparecen en el panel del propietario, donde cada una se puede configurar para que no genere cashback (tabaco, alcohol), no se pueda pagar con cashback (artículos que ya están en promoción) o genere su propio porcentaje en lugar del nivel del cliente (café al 10 %). La API puede crear productos y categorías y cambiarles el nombre, pero solo el propietario cambia estas reglas.
curl https://loyaltyfy.io/api/v1/catalog/products/bulk \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"products": [
{ "sku": "4840001", "name": "Marlboro Gold", "category": "TOB", "category_name": "Tobacco", "price": 6500 },
{ "sku": "1001", "name": "Latte", "category": "COF", "category_name": "Coffee", "price": 4500 },
{ "sku": "2040", "name": "Zeama", "category": "KIT", "category_name": "Kitchen", "price": 6500 }
]
}'
# { "object": "catalog_sync", "upserted": 3 }Después envía line_items con cada ticket. Una línea puede llevar category o solo sku: buscamos la categoría en el catálogo. Los totales de las líneas se ajustan al importe del ticket, así que los descuentos hechos en la caja se reparten de forma proporcional. Las líneas de categorías sin reglas generan cashback según el nivel del cliente.
curl https://loyaltyfy.io/api/v1/transactions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "CHK-2026-000130",
"member": "2000111703779",
"amount": 17500,
"line_items": [
{ "sku": "4840001", "quantity": 1, "unit_price": 6500 },
{ "sku": "1001", "quantity": 1, "unit_price": 4500 },
{ "sku": "2040", "quantity": 1, "unit_price": 6500 }
]
}'
# Tobacco earns nothing, Coffee has its own 10%, Kitchen earns at the guest's level (5%):
# 0 + 450 + 325 = "cashback_earned": 775Pago con cashback mediante una reserva
Con En el ticket (la opción por defecto), la caja envía redeem_amount con el ticket cerrado. Con Con reserva, la caja aparta el importe cuando el cliente pide pagar con cashback, y el ticket cerrado lo confirma. Si el ticket no llega nunca (se cayó la red, se canceló la venta), la reserva se libera cuando pasa el tiempo que fijó el propietario, y el cliente conserva el dinero. Nunca se aplica un descuento sin descontarlo del saldo.
- 1Reserva
POST /v1/holdscon el socio, el importe y el total del ticket. Se aplican los mismos topes que a un ticket. La respuesta incluye elidde la reserva. - 2Captura
POST /v1/transactionsconhold_id. Si no envíasredeem_amount, se usa la reserva completa; también puedes enviar un importe menor. - 3O libera
POST /v1/holds/{id}/releasecuando se cancela la venta. Liberar dos veces no tiene efecto.
curl https://loyaltyfy.io/api/v1/holds \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: hold-R3-000131" \
-d '{ "member": "2000111703779", "amount": 3000, "check_amount": 20000, "register_id": "R3" }'
# at payment: the check captures it
curl https://loyaltyfy.io/api/v1/transactions \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "external_id": "000131", "register_id": "R3", "member": "2000111703779",
"amount": 20000, "hold_id": "5b0d6c1e-8f3a-4d7e-9a61-2c4b8e0f7d19" }'cashback.available del socio, en los límites y en la propia app de caja del local, así que el mismo dinero no se puede gastar dos veces.Números de ticket por caja
Si cada caja numera los tickets desde 1, elige Cada caja numera los suyos. En ese caso register_id es obligatorio y guardamos el ticket como <register_id>:<external_id>, por ejemplo R3:15. Usa ese id combinado para leer, anular o devolver el ticket.
Tickets enviados tarde
Tras un corte, la caja envía su cola. Con un límite de, por ejemplo, 72 horas, los tickets cuyo closed_at sea más antiguo se rechazan con check_too_old, para que una caja restaurada desde una copia de seguridad antigua no llene el local de tickets viejos. Un closed_at más de 10 minutos en el futuro siempre se rechaza.
Límites de canje
Además del porcentaje del ticket que fija el programa, el propietario puede fijar el máximo de cashback por ticket y por cliente al día (todas las cajas juntas, según la zona horaria del local). Un ticket que pide más canjea lo permitido y la respuesta muestra el redeem_amount real; una reserva que pide más se rechaza con limited_by. Usa calcular para mostrar antes al cliente la cifra exacta.
Hay que escanear la tarjeta
Con esta regla, un ticket, una visita, una reserva o una recompensa para una tarjeta solo se aceptan si la misma clave envió esa tarjeta a POST /v1/scans poco antes. Un número de tarjeta tecleado a mano en la caja se rechaza con scan_required. Envía la salida sin procesar del escáner a /v1/scans y luego sigue como siempre.
Ajustes manuales
Desactivados por defecto. Si el propietario los permite, una clave con el permiso members:adjust puede sumar o restar cashback indicando un motivo, hasta el límite por ajuste que fijó el propietario. Cada ajuste queda en el historial del socio y envía balance.adjusted.
curl https://loyaltyfy.io/api/v1/members/2000111703779/adjustments \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{ "external_id": "ADJ-2026-0009", "amount": 2000, "reason": "Goodwill after a complaint" }'
# a negative amount takes cashback away