Guía de integración con Loyenta (API v1)
Para: equipos de desarrollo que conectan su sistema (POS, agenda, facturación, app propia) con Loyenta.
Un solo contrato para todos. Esta guía es igual para cualquier sistema, sea de BUILDEV o de terceros. Loyenta no tiene endpoints, campos ni reglas especiales para ningún sistema: cada integración se adapta a este contrato, no al revés. Si un sistema necesita algo que el contrato no cubre, se evalúa como mejora general de la API para todos. Versión del contrato: v1 del 29-10-2026.
Estado: la API v1 está implementada: llaves de prueba (
loy_test_) y de producción (loy_live_) que cada partner crea en el portal de desarrolladores, inscripción, tarjeta web, Google Wallet, panel del negocio y límite de peticiones. Todavía no existen: las llaves por negocio (§3.2) y los webhooks (§9). Cualquier cambio del contrato se anotará en la sección Cambios.
1. Qué hace Loyenta y qué hace tu sistema
| Loyenta | Tu sistema |
|---|---|
| Guarda los programas, los clientes inscritos, los sellos y las recompensas | Avisa a Loyenta cuando ocurre algo que puede dar sello: una venta pagada, un servicio realizado, una cita atendida |
| Decide si corresponde sello y cuántos, según las reglas que configuró el negocio | No calcula sellos: solo envía la operación tal como ocurrió |
| Actualiza la tarjeta web y Google Wallet (Apple Wallet más adelante) | Muestra al negocio el enlace o QR de inscripción y, si quiere, el progreso del cliente |
| Valida los canjes y evita duplicados | Puede registrar canjes desde su propia interfaz |
Regla de oro: Loyenta es la fuente oficial del estado de fidelización. Tu sistema nunca guarda "el saldo de sellos" como dato propio: si lo necesita, lo consulta.
2. Conceptos
| Concepto | Qué es |
|---|---|
Negocio (business) |
Cada comercio que usa Loyenta. Sus datos están aislados de los demás negocios. |
Programa (program) |
La tarjeta de sellos del negocio: marca, sellos requeridos, recompensa y reglas. |
Cliente (customer) |
El cliente final del negocio. Se identifica por celular (formato E.164, por ejemplo +573001234567). |
Inscripción (enrollment) |
Un cliente inscrito en un programa, con su tarjeta. Solo existe si el cliente aceptó las condiciones y el tratamiento de datos. |
Operación (operation) |
Algo que ocurrió en tu sistema: una venta, un servicio o una visita. Loyenta decide si otorga sellos. |
Recompensa (reward) |
Se desbloquea al completar la tarjeta. Queda disponible hasta que se canjea. |
| Partner | Empresa de software que integra Loyenta para muchos negocios (por ejemplo, un POS o una app de agenda). |
3. Dos formas de conectarse
3.1 Como partner (recomendado para software con muchos negocios)
Tu sistema tiene una credencial de partner y crea y administra los negocios por API. Es la forma de vender Loyenta como función de tu producto (por ejemplo, incluida en un plan de pago).
- Te registras tú mismo en el portal de desarrolladores (
https://loyenta.buildev.co/desarrolladores) con el correo de tu empresa y un código que te llega. Ahí creas tus llaves de prueba y, cuando estés listo, de producción. No tienes que pedirle nada a BUILDEV. - Cuando un usuario de tu sistema activa la fidelización, llamas a
POST /v1/partner/businesses. - Loyenta crea el negocio y envía al dueño una invitación por correo para configurar su marca y su programa en el panel de Loyenta.
- Desde ese momento, tu credencial puede operar sobre ese negocio.
3.2 Con la llave de un negocio (integraciones puntuales)
El dueño del negocio genera una llave en el panel de Loyenta (Integraciones → Nueva llave) y la pega en la configuración de tu sistema. Esa llave solo sirve para ese negocio. (Aún no disponible: por ahora, usa la forma de partner.)
4. Ambientes y autenticación
Hay una sola URL para todo: https://loyenta.buildev.co/v1. El modo lo decide la llave:
| Modo | Prefijo de llave | Para qué |
|---|---|---|
| Prueba | loy_test_ |
Desarrollar y probar. Los negocios que crea son de prueba (livemode: false) y en su panel aparece la etiqueta "Datos de prueba". |
| Producción | loy_live_ |
Negocios reales (livemode: true). Sus dueños reciben correos de verdad. |
- Las llaves se crean, rotan y revocan en el portal de desarrolladores. La llave completa se muestra una sola vez, al crearla; Loyenta solo guarda su huella y no puede volver a mostrártela.
- Puedes tener hasta dos llaves activas por modo, para rotarlas sin cortar el servicio: creas la nueva, la despliegas y revocas la vieja.
- Una llave de prueba nunca ve los negocios, operaciones ni canjes de producción, y al revés: responde
404. El mismoexternal_refosource_refen los dos modos son registros distintos.
La API (/v1), las páginas del cliente (/i, /t) y el panel (/panel) viven en el mismo dominio. Los enlaces que devuelve la API (enrollment_url, card.web_url, url del panel) ya vienen completos: úsalos tal cual, sin armarlos a mano.
Authorization: Bearer loy_test_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Reglas de seguridad obligatorias:
- Las llamadas a Loyenta se hacen desde tu backend. Nunca pongas la llave en una app móvil, un frontend web o un repositorio. Una llave expuesta permite registrar operaciones en nombre de tus negocios.
- Guarda la llave como secreto (variable de entorno o gestor de secretos).
- Si una llave se filtra, revócala en el portal de desarrolladores y genera otra. Pueden convivir dos llaves activas mientras rotas.
- Loyenta no confía en el
business_idque envías: verifica que tu credencial tenga permiso sobre ese negocio. Si no lo tiene, la respuesta es404.
5. Flujo típico
sequenceDiagram
participant N as Negocio (en tu sistema)
participant S as Tu backend
participant L as Loyenta
participant C as Cliente final
N->>S: Activa "Fidelización"
S->>L: POST /v1/partner/businesses
L-->>N: Invitación por correo (configura marca y programa)
N->>S: Registra a un cliente o le comparte el QR del negocio
S->>L: POST /v1/enrollment-invitations (celular del cliente)
L-->>S: enrollment_url
S-->>C: Envía el enlace (WhatsApp, SMS) o lo muestra como QR
C->>L: Acepta condiciones, deja su correo y obtiene la tarjeta (web / Google Wallet)
N->>S: Registra una venta pagada
S->>L: POST /v1/operations (Idempotency-Key)
L-->>S: stamps_awarded, saldo, recompensas
L-->>C: La tarjeta se actualiza sola
6. Endpoints
Todos los POST aceptan el header Idempotency-Key (ver §7).
6.1 Partner: negocios
POST /v1/partner/businesses
Crea el negocio, o lo devuelve si ya existe con ese external_ref.
{
"external_ref": "user_cl9x8…",
"trade_name": "Barbería El Corte",
"legal_name": "El Corte S.A.S.",
"nit": "900123456-7",
"owner": { "name": "Laura Gómez", "email": "laura@elcorte.co" },
"business_type": "barberia"
}
Respuesta 201 (o 200 si ya existía):
{
"id": "biz_01J…",
"external_ref": "user_cl9x8…",
"trade_name": "Barbería El Corte",
"livemode": false,
"status": "active",
"setup": { "branding_done": false, "program_done": false },
"panel_invitation_sent": true
}
GET /v1/partner/businesses/{id}
Devuelve el estado del negocio. Mientras setup.program_done sea false, no hay programa activo y las operaciones se rechazan con program_not_configured.
POST /v1/partner/businesses/{id}/suspend · POST /v1/partner/businesses/{id}/resume
Se usan cuando cambia la suscripción en tu sistema. Mientras el negocio está suspendido:
- no se otorgan sellos;
- las tarjetas muestran "programa en pausa";
- los datos se conservan.
POST /v1/partner/businesses/{id}/panel-sessions
Devuelve un enlace para que el dueño abra el panel de su negocio desde tu sistema, sin volver a iniciar sesión. Úsalo, por ejemplo, en un botón "Personalizar mi tarjeta".
{
"url": "https://loyenta.buildev.co/panel/acceso?token=…",
"expires_at": "2026-11-02T15:09:05Z"
}
- El enlace es de un solo uso y vence en 5 minutos: pídelo justo cuando el usuario toque el botón, no lo guardes.
- Entra al panel con la sesión del dueño del negocio (
owner.email). Solo ve y cambia ese negocio y los demás que sean suyos. - Abierto así, el panel no muestra "Salir": le indica al dueño que cierre la pestaña para volver a tu app.
- Solo funciona con negocios a los que tu credencial tiene acceso; si no,
404. - Llámalo solo desde tu backend y solo para el usuario de tu sistema que administra ese negocio.
6.2 Programas
GET /v1/programs?business_id=biz_…
Devuelve la configuración pública del programa: nombre, sellos requeridos, recompensa, enlace y QR de inscripción del negocio. Sirve para mostrarla en tu interfaz.
{
"data": [{
"id": "prog_01J…",
"name": "Tarjeta de sellos",
"stamps_required": 10,
"reward_description": "Un corte gratis",
"enrollment_url": "https://loyenta.buildev.co/i/barberia-el-corte",
"enrollment_qr_png": "https://loyenta.buildev.co/i/barberia-el-corte/qr.png"
}]
}
6.3 Clientes e inscripciones
La inscripción la acepta el cliente, no el negocio. La Ley 1581 exige la autorización del titular, y Loyenta debe poder probarla. Hay dos caminos:
A. Invitación (recomendado). Tu sistema pide un enlace personalizado y se lo hace llegar al cliente. El cliente lo abre, ve las condiciones prellenadas con su nombre y celular, deja su correo y acepta.
POST /v1/enrollment-invitations
{
"business_id": "biz_…",
"program_id": "prog_…",
"customer": { "name": "Ana Pérez", "phone": "+573001234567", "external_ref": "client_cm1…" }
}
{
"enrollment_url": "https://loyenta.buildev.co/i/barberia-el-corte?inv=4f9c…",
"expires_at": "2026-11-03T23:59:59-05:00",
"already_enrolled": false
}
program_ides opcional: si no lo envías, se usa el programa del negocio.- La invitación vale 7 días y sirve una sola vez.
- Si ese celular ya está inscrito, responde
already_enrolled: true,expires_at: nully enenrollment_urlel enlace de su tarjeta. Puedes enviárselo igual: el cliente llega a su tarjeta. - Si el negocio aún no configuró su programa, responde
409concode: "program_not_configured".
Para no tener que pedir una invitación por cliente, también puedes mostrar el QR del negocio (enrollment_qr_png de §6.2): el cliente lo escanea y se inscribe solo, escribiendo su nombre y celular.
B. Inscripción directa con evidencia. Solo si tu propia interfaz le muestra al cliente el texto legal de Loyenta y el cliente lo acepta allí mismo. Debes enviar la evidencia: la versión del texto, la fecha, el canal y la IP.
POST /v1/enrollments
{
"business_id": "biz_…",
"program_id": "prog_…",
"customer": { "name": "Ana Pérez", "phone": "+573001234567", "email": "ana@correo.co", "external_ref": "client_cm1…" },
"consent": {
"policy_version": "2026-11-01",
"accepted_at": "2026-11-02T10:15:00-05:00",
"channel": "partner_app",
"ip": "190.0.0.1",
"marketing_opt_in": false
}
}
Responde 201 con la inscripción (el mismo objeto de abajo), o 200 si ese celular ya estaba inscrito en el negocio.
GET /v1/enrollments/{id} · GET /v1/enrollments?business_id=…&phone=+57…
GET /v1/enrollments/{id} devuelve una inscripción:
{
"id": "enr_…",
"business_id": "biz_…",
"program_id": "prog_…",
"customer": { "name": "Ana Pérez", "phone": "+573001234567", "external_ref": "client_cm1…" },
"stamps": { "current": 7, "required": 10, "carryover": 0 },
"rewards_available": [],
"card": {
"web_url": "https://loyenta.buildev.co/t/…",
"google_wallet_url": "https://pay.google.com/gp/v/save/…"
},
"created_at": "2026-11-02T10:15:00.000Z"
}
GET /v1/enrollments?business_id=… devuelve la lista en data ({ "data": [ … ] }). Con &phone= trae como máximo una: la de ese celular, o data vacío si no está inscrito. Es la forma de saber si un cliente ya tiene tarjeta. Recuerda codificar el + del celular en la URL (%2B57…).
stamps.currentes lo que muestra la tarjeta (de 0 arequired);carryoverson los sellos de más que pasan a la siguiente tarjeta al canjear.card.google_wallet_urlesnullsi Google Wallet no está configurado en ese ambiente.
6.4 Operaciones (el corazón de la integración)
POST /v1/operations
Envía lo que ocurrió, no cuántos sellos dar.
{
"business_id": "biz_…",
"source_ref": "txn_clx7…",
"type": "purchase_paid",
"occurred_at": "2026-11-02T15:04:05-05:00",
"customer": { "phone": "+573001234567", "external_ref": "client_cm1…" },
"amount": { "value": 45000, "currency": "COP" },
"items": [
{ "ref": "svc_1", "name": "Corte clásico", "category": "service", "quantity": 1, "unit_price": 30000 },
{ "ref": "svc_2", "name": "Cera", "category": "product", "quantity": 1, "unit_price": 15000 }
],
"metadata": { "tip": 5000 }
}
Tipos de operación (type):
| Tipo | Cuándo |
|---|---|
purchase_paid |
Una venta cobrada. |
service_completed |
Un servicio realizado. |
visit_attended |
Una cita o visita atendida. |
Campos:
source_ref: el id de la operación en tu sistema. Es único por integración, y Loyenta lo usa para no otorgar sellos dos veces.amount.value: el monto que cuenta para la fidelización, en pesos y sin propina.items[].category:serviceoproduct. El negocio puede configurar que solo cuenten los servicios.
Respuesta 201:
{
"id": "op_…",
"source_ref": "txn_clx7…",
"status": "accepted",
"result": {
"enrollment_id": "enr_…",
"stamps_awarded": 1,
"stamps": { "current": 8, "required": 10, "carryover": 0 },
"rewards_unlocked": []
}
}
Si ya habías enviado ese source_ref, responde 200 con el resultado original (ver §7).
Una operación válida que no da sello no es un error. Responde 201 con status: "rejected" y un rejection_code:
{
"id": "op_…",
"source_ref": "txn_clx8…",
"status": "rejected",
"rejection_code": "customer_not_enrolled",
"enrollment_url": "https://loyenta.buildev.co/i/barberia-el-corte",
"result": { "enrollment_id": null, "stamps_awarded": 0, "stamps": null, "rewards_unlocked": [] }
}
enrollment_url solo viene con customer_not_enrolled: es la página de inscripción del negocio, para ofrecérsela al cliente.
rejection_code |
Qué significa | Qué hacer |
|---|---|---|
customer_not_enrolled |
El cliente no está inscrito. Esa operación no dará sellos aunque el cliente se inscriba después: los sellos cuentan desde la inscripción | Mostrar u ofrecer el enrollment_url que viene en la respuesta |
program_not_configured |
El negocio aún no configuró su programa | Recordarle al negocio que termine la configuración |
not_eligible |
No cumple las reglas (tipo, categoría, monto mínimo o días) | Nada: es lo esperado |
daily_limit_reached |
El cliente ya alcanzó el tope de sellos del día | Nada |
business_suspended |
El negocio está suspendido | Revisar la suscripción |
GET /v1/operations/{id} · GET /v1/operations?business_id=…&source_ref=…
Consulta el resultado de una operación, por ejemplo después de un timeout. El de {id} devuelve el mismo objeto que el POST; el listado lo devuelve en data.
POST /v1/operations/{id}/reversal
Anula una operación, por ejemplo una venta borrada o devuelta.
{ "reason": "Venta eliminada en el sistema de origen" }
Responde 200 con la operación, ahora con status: "reversed" y el detalle de la anulación:
{
"id": "op_…",
"source_ref": "txn_clx7…",
"status": "reversed",
"result": { "enrollment_id": "enr_…", "stamps_awarded": 1, "stamps": { "current": 7, "required": 10, "carryover": 0 }, "rewards_unlocked": [] },
"reversal": { "reason": "Venta eliminada en el sistema de origen", "reversed_at": "2026-11-02T21:00:00.000Z", "stamps_removed": 1, "discrepancy": 0 }
}
stamps_removed son los sellos que se restaron de verdad; discrepancy, los que no se pudieron restar porque la recompensa ya se había canjeado.
- Se restan los sellos que otorgó esa operación. El saldo nunca queda negativo.
- Si la recompensa que desbloqueó aún no se canjea, deja de estar disponible.
- Si ya se canjeó, el canje no se deshace. La diferencia queda registrada para que el negocio la revise.
- Anular dos veces devuelve el mismo resultado (es idempotente).
6.5 Recompensas y canjes
GET /v1/enrollments/{id}/rewards
Lista las recompensas disponibles del cliente, en data:
{
"data": [{
"id": "rwd_…",
"enrollment_id": "enr_…",
"description": "Un corte gratis",
"status": "available",
"unlocked_at": "2026-11-02T20:04:06.000Z",
"redeemed_at": null
}]
}
POST /v1/rewards/{id}/redemptions
Canjea una recompensa. El cuerpo es opcional.
{ "source_ref": "canje_123", "redeemed_by": "Laura (caja)" }
Respuesta 201: la recompensa ya canjeada y cómo queda la tarjeta.
{
"reward": { "id": "rwd_…", "enrollment_id": "enr_…", "description": "Un corte gratis", "status": "redeemed", "unlocked_at": "…", "redeemed_at": "2026-11-05T16:30:00.000Z" },
"stamps": { "current": 2, "required": 10, "carryover": 0 }
}
- Responde
201la primera vez. - Un segundo canje de la misma recompensa responde
409concode: "reward_already_redeemed". - Al canjear, la tarjeta pasa al siguiente ciclo y conserva el excedente (por ejemplo, de 10/10 a 2/10).
7. Idempotencia y reintentos
Las redes fallan. Loyenta está diseñada para que reintentar sea seguro:
- En
POST /v1/operations, el par (tu integración,source_ref) es único. Si reenvías la misma venta, Loyenta devuelve el resultado original (200) y no otorga sellos otra vez. - En los demás
POST, envíaIdempotency-Key: <uuid>. La misma clave con el mismo cuerpo devuelve la misma respuesta durante 24 horas. - Política sugerida de reintentos:
- Ante un timeout,
429o5xx, reintenta con espera creciente: 1 s, 5 s, 30 s, 2 min y 10 min. - Ante un
4xx(salvo429), no reintentes: el problema está en los datos o en los permisos.
- Ante un timeout,
- Si tu sistema funciona sin conexión, guarda la operación en una cola y envíala cuando haya internet, con su
occurred_atoriginal. Las reglas usan esa fecha.
8. Errores
Formato application/problem+json:
{
"type": "https://loyenta.buildev.co/errores/validation_error",
"title": "Datos inválidos",
"status": 400,
"code": "validation_error",
"detail": "customer.phone: debe estar en formato E.164 (+57…)",
"request_id": "req_01J…"
}
| HTTP | code |
Causa |
|---|---|---|
| 400 | validation_error |
Faltan campos o el formato es incorrecto |
| 401 | invalid_api_key |
Llave inválida o revocada |
| 403 | insufficient_scope |
La llave no tiene ese permiso |
| 404 | not_found |
No existe o tu credencial no tiene acceso a ese negocio |
| 409 | reward_already_redeemed, idempotency_conflict, program_not_configured |
Canje repetido, la misma clave con un cuerpo distinto, o una inscripción o invitación antes de que el negocio configure su programa |
| 429 | rate_limited |
Demasiadas peticiones: más de 600 por minuto con la misma llave (ver los headers RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset y Retry-After), o más de 100 negocios nuevos en 24 horas por modo |
| 5xx | internal_error |
Error de Loyenta: reintenta |
Incluye siempre el request_id cuando reportes un problema.
9. Webhooks (opcional)
Si quieres enterarte de lo que pasa sin consultar, registra una URL en el panel (Integraciones → Webhooks).
| Evento | Cuándo |
|---|---|
enrollment.created |
Un cliente completó su inscripción (por ejemplo, desde tu invitación) |
stamp.awarded |
Se otorgaron sellos |
reward.unlocked |
El cliente completó su tarjeta |
reward.redeemed |
Se canjeó una recompensa |
operation.rejected / operation.reversed |
Una operación fue rechazada o anulada |
business.suspended / business.resumed |
Cambió el estado del negocio |
Formato:
{
"id": "evt_…",
"type": "reward.unlocked",
"occurred_at": "2026-11-02T20:04:06Z",
"business_id": "biz_…",
"data": { "enrollment_id": "enr_…", "reward_id": "rwd_…", "customer_external_ref": "client_cm1…" }
}
Verificación de la firma:
- Cada entrega trae el header
X-Loyenta-Signature: t=<unix>,v1=<firma>. firma = hex(HMAC-SHA256(secreto_del_webhook, t + "." + cuerpo_crudo)).- Rechaza los eventos con
tde más de 5 minutos. - Deduplica por
id. - Responde
2xxen menos de 10 s. - Loyenta reintenta durante unas 24 horas y luego marca la entrega como fallida (se puede reenviar desde el panel).
import { createHmac, timingSafeEqual } from "node:crypto";
export function firmaValida(cuerpoCrudo: string, cabecera: string, secreto: string): boolean {
const partes = Object.fromEntries(cabecera.split(",").map((p) => p.split("=") as [string, string]));
if (Math.abs(Date.now() / 1000 - Number(partes.t)) > 300) return false;
const esperada = createHmac("sha256", secreto).update(`${partes.t}.${cuerpoCrudo}`).digest("hex");
return partes.v1?.length === esperada.length && timingSafeEqual(Buffer.from(partes.v1), Buffer.from(esperada));
}
10. Personalización por negocio
Cada negocio configura su programa en el panel de Loyenta. El enlace llega en la invitación, y el dueño también puede entrar con su correo y un código de un solo uso. Tu sistema no necesita construir esta configuración: basta con abrir el panel con un enlace de POST /v1/partner/businesses/{id}/panel-sessions (§6.1), así el dueño no vuelve a iniciar sesión. Más adelante habrá endpoints de partner para hacerlo desde tu interfaz.
10.1 Marca
| Campo | Requisito | Dónde se ve |
|---|---|---|
| Nombre comercial | Máximo 20 caracteres recomendado | Encabezado de la tarjeta y de la Wallet |
| Logo | Se sube desde el panel (JPG o PNG; el panel lo recorta en cuadrado y lo deja en 660×660 px) o se da un enlace público. Google lo recorta en círculo | Tarjeta web y Wallet |
| Color principal | #RRGGBB. Si es un color claro, el texto pasa solo a oscuro para que se lea |
Fondo de la tarjeta y de la Wallet |
| Color secundario | #RRGGBB |
Tarjeta web y página de inscripción |
| Ícono de los sellos | Uno de un catálogo (chulo, estrella, corazón, tijeras, café, flor…) o una imagen propia subida en el panel, distinta del logo | Sellos de la tarjeta web y de la Wallet |
| Contacto | Teléfono, WhatsApp, dirección y redes | Detalle de la tarjeta |
Lo que el negocio NO puede cambiar en Google Wallet (lo fija Google): la disposición de la tarjeta, la tipografía, la posición del QR y del logo. Loyenta muestra los sellos como una imagen generada con el color del negocio. En la tarjeta web el diseño es más libre.
10.2 Programa y reglas
| Opción | Ejemplo |
|---|---|
| Nombre del programa | "Club El Corte" |
| Sellos requeridos | 10 |
| Recompensa | "Un corte gratis" |
| Cómo se gana | 1 sello por visita, 1 por cada $20.000 o 1 por cada servicio |
| Qué cuenta | Solo servicios, solo ciertos servicios o todo |
| Monto mínimo y máximo | Desde $20.000; se cuenta hasta $200.000 |
| Topes | Máximo 1 sello por día por cliente; máximo 3 por venta |
| Días válidos | De lunes a jueves |
| Vencimientos | La recompensa vence en 30 días; los sellos, a los 180 días sin visitas |
| Condiciones | Texto libre del negocio (se genera un borrador a partir de las reglas) |
Por eso tu integración envía la operación completa: tipo, monto e ítems con su categoría. Así cualquier regla que configure el negocio funciona sin cambiar tu código.
11. Lista de verificación de una integración
- Las llamadas salen del backend, y la llave está guardada como secreto.
- Los celulares se envían en formato E.164 (
+57+ 10 dígitos). - Cada operación lleva un
source_refestable (el id de la venta en tu sistema). - Hay reintentos con espera creciente. Un reintento nunca otorga sellos dobles (probado en sandbox).
- Al borrar o devolver una venta, se llama a
/reversal. - Se manejan
customer_not_enrolled(ofrecer la inscripción) yprogram_not_configured. - El monto enviado no incluye la propina, y los ítems traen
category. - Si usas webhooks, se verifica la firma y se deduplica por
id. - La integración se probó completa con llaves de prueba antes de crear las de producción.
12. Cambios
| Fecha | Cambio |
|---|---|
| 2026-10-27 | Primer borrador del contrato v1. |
| 2026-10-27 | Nuevo POST /v1/partner/businesses/{id}/panel-sessions: enlace de un solo uso para abrir el panel desde el sistema del partner (§6.1 y §10). |
| 2026-10-29 | Los partners se registran solos y crean sus llaves de prueba y de producción en el portal de desarrolladores (§3.1 y §4); BUILDEV ya no las entrega a mano. Llega loy_live_ y la separación de datos por modo: los negocios traen el campo nuevo livemode (§6.1). Se activa el límite de peticiones (429, §8). La guía se publica en /desarrolladores/guia. |
| 2026-10-28 | El sandbox pasa a PostgreSQL y queda listo para montarse en un servidor (§4). Se documentan respuestas que ya existían, sin cambiar el contrato: listados en data, enrollment_url en las operaciones con customer_not_enrolled, el objeto reversal, la respuesta del canje (reward y stamps) y already_enrolled en las invitaciones (§6). En el panel: logo subido, ícono o imagen propia de los sellos y contraste automático del texto (§10.1). |