Reten Docs
Estados de Ciclo de Vida de Comercios

Estados de Ciclo de Vida de Comercios

Consume y actualiza el estado de ciclo de vida de retención de cada comercio — identificadores duales, frescura, y comportamiento con datos sin resolver.

Los estados de ciclo de vida (lifecycle states) son la foto vigente del estado de retención de cada comercio, calculada por el motor de Reten. Cada estado responde a la pregunta: "¿en qué punto del ciclo de retención está este comercio, según qué configuración, y desde cuándo?".

Esta sección documenta el contrato de consumo y escritura de esos estados.

Capabilities

La API expone cinco capabilities sobre el recurso commerce-lifecycle-snapshots:

Autenticación

Todos los endpoints de estados de ciclo de vida aceptan dos métodos de autenticación:

MétodoEncabezadosUso típico
API Keyx-api-key: YOUR_API_KEYIntegradores externos / partners. La key resuelve el tenant automáticamente — no envíes x-tenant-id.
JWTAuthorization: Bearer <token> + x-tenant-id: <tenant-id>Uso interno / panel de administración.

Permisos requeridos:

PermisoCapabilities
commerce-lifecycle-snapshot:viewlistar, consultar por comercio, lookup por lote
commerce-lifecycle-snapshot:createupsert individual, upsert por lote
activity-generation-config:viewdescubrir los AGC disponibles (ver Setup de AGC)

Los ejemplos de esta sección usan el método API Key (x-api-key), que es el camino esperado para un integrador externo. Si consumes desde el panel interno con un token JWT, reemplaza ese encabezado por Authorization: Bearer <token> + x-tenant-id: <tenant-id>.

El recurso: snapshot de estado

Un estado de ciclo de vida (snapshot) se representa con esta forma canónica. Las superficies que devuelven el recurso "crudo" (list, lookup por lote, upsert) usan exactamente estos campos:

{
  "id": "5d000000-0000-4000-8000-000000000001",
  "commerce_id": "c0000000-0000-4000-8000-000000000001",
  "external_id": "SV-001",
  "activity_generation_config_id": "a1b2c3d4-0000-4000-8000-000000000001",
  "code": "AT_RISK_MONTHLY",
  "label": "En riesgo (mensual)",
  "stage": "ENGAGEMENT",
  "last_computed_at": "2026-06-10T06:00:00.000Z",
  "state_changed_at": "2026-05-22T06:00:00.000Z"
}
CampoTipoDescripción
idUUIDIdentificador único del snapshot.
commerce_idUUIDUUID del comercio en Reten.
external_idstring | nullCódigo externo del comercio en tu sistema (ej. "SV-001"). Denormalizado al momento del upsert; null si el comercio no tiene código externo.
activity_generation_config_idUUIDAGC bajo la cual se calculó este estado. Ver identificador de AGC.
codestringCódigo crudo del estado de retención (ej. "AT_RISK_MONTHLY"). Corresponde a un UserRetentionStatusConfig del tenant.
labelstringEtiqueta legible del estado, derivada (denormalizada) desde la config del código. La fuente de verdad es la config.
stageenumEtapa del ciclo de vida. Ver etapas.
last_computed_atISO 8601Cuándo el motor calculó este snapshot por última vez. Ver frescura.
state_changed_atISO 8601Cuándo cambió por última vez el estado visible (stage, code). Ver frescura.

Modelo de identificadores (dual)

Un comercio puede referenciarse de dos formas equivalentes a lo largo de toda la API de estados:

  • UUID de Reten (commerce_id) — el identificador interno, ej. c0000000-0000-4000-8000-000000000001.
  • Código externo (external_id) — el código del comercio en tu propio sistema, ej. "SV-001".

Cada endpoint declara cuál(es) acepta:

EndpointUUIDCódigo externoRegla
Consultar por comercio (GET /:commerceIdentifier)El path acepta cualquiera de los dos, indistintamente.
Lookup por lote✅ (commerce_ids)✅ (external_ids)Un solo tipo por request (no se mezclan).
Upsert por lote✅ (commerce_id)✅ (external_id)Homogéneo: todos los items del lote usan el mismo tipo.
Upsert individual✅ (commerce_id)Solo UUID.

Usar el código externo evita tener que mantener un mapeo de UUIDs de Reten en tu sistema: escribes y lees usando los mismos códigos que ya manejas internamente.

Etapas del ciclo de vida (stage)

El campo stage toma uno de tres valores posibles:

  • ACTIVATION
  • ENGAGEMENT
  • RESURRECTION

El par (stage, code) define el estado visible de un comercio

Frescura: last_computed_at vs state_changed_at

Son dos marcas de tiempo con semánticas distintas; entender la diferencia es clave para consumir estos datos correctamente.

CampoQué respondeQuién lo controla
last_computed_at"¿Cuándo se recalculó este estado por última vez?"Lo provee el caller (el motor) en cada upsert.
state_changed_at"¿Desde cuándo el comercio está en este estado visible (stage, code)?"Lo gestiona Reten automáticamente.

Reglas de state_changed_at:

  • Al crear el snapshot, se inicializa igual a last_computed_at.
  • Al actualizar, solo cambia si el par (stage, code) es distinto al que ya existía. Si solo cambia el label o el last_computed_at pero (stage, code) se mantiene, state_changed_at no se mueve.

Ejemplo de la evolución de un mismo comercio:

Eventostage / codelast_computed_atstate_changed_at resultante
Cálculo 1 (alta)ENGAGEMENT / AT_RISK_MONTHLY2026-05-22T06:00Z2026-05-22T06:00Z (inicializado)
Cálculo 2 (mismo estado)ENGAGEMENT / AT_RISK_MONTHLY2026-06-01T06:00Z2026-05-22T06:00Z (sin cambios)
Cálculo 3 (cambió la etapa)RESURRECTION / WIN_BACK2026-06-10T06:00Z2026-06-10T06:00Z (actualizado)

Usa last_computed_at para responder "¿qué tan reciente es este dato?" y state_changed_at para "¿cuánto tiempo lleva el comercio en este estado?" (ej. "lleva 19 días en riesgo").

Comportamiento con datos sin resolver o stale

Sin resolver (unresolved)

Un identificador está sin resolver cuando no existe un snapshot para él. La API agrupa por igual dos casos (intencionalmente — el resultado es inobtenible de cualquier forma):

  • El comercio no existe en el tenant.
  • El comercio existe pero aún no tiene estado calculado.

Cómo se manifiesta según el endpoint:

EndpointComportamiento ante un identificador sin resolver
Lookup por loteEl identificador aparece en el array unresolved. La request no falla.
Consultar por comercioResponde 404 Not Found.
ListarSimplemente no aparece en data (el list solo devuelve estados que existen).

Stale (datos viejos)

La API no marca frescura ni elimina estados viejos: un snapshot vive hasta que el motor lo sobreescriba. La "frescura" la decide el consumidor comparando last_computed_at contra el momento actual y aplicando su propio umbral (ej. "considero stale todo lo calculado hace más de 24h").

Para encontrar estados recientes sin recorrer todo, el list acepta el filtro state_changed_since, que devuelve solo los estados cuyo state_changed_at es posterior a una fecha dada.

Identificador de AGC y flujo de setup

El estado de ciclo de vida es por par (comercio, AGC), no único por comercio. Una "Activity Generation Config" (AGC) es la configuración bajo la cual el motor calcula los estados; un mismo comercio puede tener un estado distinto bajo cada AGC.

Por eso el parámetro activity_generation_config_id aparece en todos los endpoints — de lectura y de escritura:

  • Es opcional en lectura. Si lo omites, obtienes los estados de todas las AGC del comercio/tenant.
  • Es importante si quieres evitar "mezclar" datos. Filtrando por una AGC concreta te aseguras de leer solo los estados calculados bajo esa configuración, sin entremezclar resultados de otras AGC.
  • Es obligatorio en escritura (upsert individual y por lote), porque un estado siempre se escribe bajo una AGC específica.

Cómo obtener los AGC IDs disponibles

Si quieres filtrar por AGC, primero descubre las que existen en tu tenant con:

GET /api/activity-generation-configs

Auth: requiere el permiso activity-generation-config:view. Responde un array; toma el id de la AGC que te interese y úsalo como activity_generation_config_id:

[
  {
    "id": "a1b2c3d4-0000-4000-8000-000000000001",
    "name": "Retención B2B",
    "description": "Ciclo de retención para clientes mayoristas"
  },
  {
    "id": "a1b2c3d4-0000-4000-8000-000000000002",
    "name": "Resurrección Q1",
    "description": "Campaña de recuperación primer trimestre"
  }
]

Si tu tenant opera con una sola AGC, puedes omitir activity_generation_config_id en las lecturas y trabajar sin él. El parámetro cobra importancia cuando coexisten múltiples AGC y no quieres mezclar sus estados.