> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Loyalty — APIs y liberación

> Todos los endpoints del módulo, su clase de SLO, el permiso que declaran y la versión en la que quedan disponibles. Es el contrato que un integrador puede planificar.

Cada endpoint declara **exactamente un permiso** (R16) y pertenece a **una sola clase de SLO**
(DEC-D7). La columna *Desde* dice en qué versión de la suite queda disponible; hasta entonces el
endpoint no existe, ni siquiera devolviendo 501.

## Cómo se lee la columna *Desde*

Todo lo de esta página pertenece a **[`R1 · Loyalty`](/releases/r1-loyalty)**, una de las cinco
entregas de [Release 1](/releases/overview) (`2026.11`). La columna *Desde* nombra la entrega, no
una versión inventada ([ADR-025](/adr/adr-025-release-model)).

Las fases **F1a / F1b / F2** siguen apareciendo en los specs, pero son **orden de construcción**
dentro del release, no versiones entregables. La columna *Orden* dice cuándo se construye:
`1` lo que bloquea al resto, `2` lo que completa la propuesta, `3` lo que depende de decisiones aún
abiertas.

## Runtime API — SLO p95 \<300 ms

Las llama una máquina en un camino caliente: un punto de venta, un carrito, un mostrador. Un
segundo de latencia acá es una fila de clientes esperando.

| Endpoint                                    | Permiso                       | Idempotencia                      | Desde          | Orden | Spec                                                                |
| ------------------------------------------- | ----------------------------- | --------------------------------- | -------------- | ----- | ------------------------------------------------------------------- |
| `POST /v1/loyalty/redemptions`              | `loyalty.redemptions.create`  | `Idempotency-Key` **obligatoria** | `R1 · Loyalty` | 1     | [FS-LOY-0006](/modules/loyalty/features/fs-loy-0006-redemptions)    |
| `POST /v1/loyalty/redemptions/{id}/reverse` | `loyalty.redemptions.reverse` | `Idempotency-Key` obligatoria     | `R1 · Loyalty` | 1     | [FS-LOY-0006](/modules/loyalty/features/fs-loy-0006-redemptions)    |
| `POST /v1/loyalty/coupons/validate`         | `loyalty.coupons.validate`    | — (sin efecto)                    | `R1 · Loyalty` | 1     | [FS-LOY-0007](/modules/loyalty/features/fs-loy-0007-coupons)        |
| `POST /v1/loyalty/coupons/redeem`           | `loyalty.coupons.redeem`      | `Idempotency-Key` obligatoria     | `R1 · Loyalty` | 1     | [FS-LOY-0007](/modules/loyalty/features/fs-loy-0007-coupons)        |
| `POST /v1/loyalty/qualifications`           | `loyalty.qualifications.read` | —                                 | `R1 · Loyalty` | 2     | [FS-LOY-0008](/modules/loyalty/features/fs-loy-0008-stacking-rules) |

## Member API — SLO p95 \<150 ms

Las llama la app o el portal del cliente final, con un token de member. Nunca con una API key de
tenant.

| Endpoint                                   | Autorización    | Desde          | Orden | Spec                                                                    |
| ------------------------------------------ | --------------- | -------------- | ----- | ----------------------------------------------------------------------- |
| `GET /v1/loyalty/members/me`               | token de member | `R1 · Loyalty` | 1     | [FS-LOY-0003](/modules/loyalty/features/fs-loy-0003-balance-projection) |
| `GET /v1/loyalty/members/me/transactions`  | token de member | `R1 · Loyalty` | 1     | [FS-LOY-0002](/modules/loyalty/features/fs-loy-0002-points-ledger)      |
| `GET /v1/loyalty/members/me/rewards`       | token de member | `R1 · Loyalty` | 1     | [FS-LOY-0005](/modules/loyalty/features/fs-loy-0005-rewards-catalog)    |
| `GET /v1/loyalty/members/me/tier`          | token de member | `R1 · Loyalty` | 2     | [FS-LOY-0010](/modules/loyalty/features/fs-loy-0010-tiers)              |
| `GET /v1/loyalty/members/me/referral-code` | token de member | `R1 · Loyalty` | 1     | [FS-LOY-0009](/modules/loyalty/features/fs-loy-0009-referrals)          |
| `POST /v1/loyalty/members/me/wallet-pass`  | token de member | `R1 · Loyalty` | 2     | [FS-LOY-0014](/modules/loyalty/features/fs-loy-0014-wallet-passes)      |

## Management API — SLO p95 \<1 s

Las llama la consola del tenant o su equipo por integración. Son pantallas, no mostradores.

| Recurso           | Endpoints                                                                                                        | Permisos                                                 | Desde          | Orden | Spec                                                                         |
| ----------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | -------------- | ----- | ---------------------------------------------------------------------------- |
| Programas         | `GET·POST /v1/loyalty/programs` · `PATCH /v1/loyalty/programs/{id}` · `GET /v1/loyalty/programs/current`         | `loyalty.programs.{read,create,update}`                  | `R1 · Loyalty` | 1     | [FS-LOY-0001](/modules/loyalty/features/fs-loy-0001-programs-and-currencies) |
| Monedas           | `GET /v1/loyalty/currencies`                                                                                     | `loyalty.currencies.read`                                | `R1 · Loyalty` | 1     | [FS-LOY-0001](/modules/loyalty/features/fs-loy-0001-programs-and-currencies) |
| Ledger            | `GET /v1/loyalty/transactions` · `POST /v1/loyalty/transactions/adjust` · `POST /v1/loyalty/transactions/revoke` | `loyalty.transactions.{read,adjust,revoke}`              | `R1 · Loyalty` | 1     | [FS-LOY-0002](/modules/loyalty/features/fs-loy-0002-points-ledger)           |
| Saldos            | `GET /v1/loyalty/balances`                                                                                       | `loyalty.balances.read`                                  | `R1 · Loyalty` | 1     | [FS-LOY-0003](/modules/loyalty/features/fs-loy-0003-balance-projection)      |
| Reglas y campañas | CRUD `/v1/loyalty/campaigns` · CRUD `/v1/loyalty/rules` · `POST /v1/loyalty/rules/{id}/publish`                  | `loyalty.{campaigns,rules}.{read,create,update,publish}` | `R1 · Loyalty` | 1     | [FS-LOY-0004](/modules/loyalty/features/fs-loy-0004-rules-engine)            |
| Recompensas       | CRUD `/v1/loyalty/rewards`                                                                                       | `loyalty.rewards.{read,create,update}`                   | `R1 · Loyalty` | 1     | [FS-LOY-0005](/modules/loyalty/features/fs-loy-0005-rewards-catalog)         |
| Cupones           | CRUD `/v1/loyalty/coupon-campaigns` · `POST /v1/loyalty/coupon-campaigns/{id}/generate`                          | `loyalty.coupons.{read,create,generate}`                 | `R1 · Loyalty` | 2     | [FS-LOY-0007](/modules/loyalty/features/fs-loy-0007-coupons)                 |
| Combinabilidad    | CRUD `/v1/loyalty/stacking-rules`                                                                                | `loyalty.stacking.{read,create,update}`                  | `R1 · Loyalty` | 2     | [FS-LOY-0008](/modules/loyalty/features/fs-loy-0008-stacking-rules)          |
| Referidos         | CRUD `/v1/loyalty/referrals` · `POST /v1/loyalty/referrals/{id}/review`                                          | `loyalty.referrals.{read,create,review}`                 | `R1 · Loyalty` | 1     | [FS-LOY-0009](/modules/loyalty/features/fs-loy-0009-referrals)               |
| Niveles           | CRUD `/v1/loyalty/tiers` · CRUD `/v1/loyalty/tier-overrides`                                                     | `loyalty.tiers.{read,create,update}`                     | `R1 · Loyalty` | 2     | [FS-LOY-0010](/modules/loyalty/features/fs-loy-0010-tiers)                   |
| Vencimientos      | `GET /v1/loyalty/expiring`                                                                                       | `loyalty.expiring.read`                                  | `R1 · Loyalty` | 1     | [FS-LOY-0011](/modules/loyalty/features/fs-loy-0011-expiring-points)         |
| Gamificación      | CRUD `/v1/loyalty/badges` · CRUD `/v1/loyalty/challenges`                                                        | `loyalty.gamification.{read,create,update}`              | `R1 · Loyalty` | 3     | [FS-LOY-0012](/modules/loyalty/features/fs-loy-0012-gamification)            |
| Descuentos        | `POST /v1/loyalty/discounts/apply`                                                                               | `loyalty.discounts.apply`                                | `R1 · Loyalty` | 3     | [FS-LOY-0013](/modules/loyalty/features/fs-loy-0013-apply-discount)          |

## Eliminar y purgar

Todo recurso de forma A expone las dos, con **permisos distintos**
([`standards/data.md`](/standards/data) §1b):

| Endpoint                                  | Qué hace                                                | Permiso                    | Reversible                   |
| ----------------------------------------- | ------------------------------------------------------- | -------------------------- | ---------------------------- |
| `DELETE /v1/loyalty/{recurso}/{id}`       | Soft delete: escribe `deleted_at`                       | `loyalty.{recurso}.delete` | Sí, con `POST /{id}/restore` |
| `DELETE /v1/loyalty/{recurso}/{id}/purge` | Hard delete: borra la fila, `409` si algo la referencia | `loyalty.{recurso}.purge`  | **No**                       |

Purgar exige haber eliminado antes. Programas y monedas son la excepción declarada: **no se
eliminan nunca**, se desactivan con `is_active`, porque sus filas del ledger deben seguir
resolviéndose para siempre.

## Reglas que aplican a todos

* **Un permiso por endpoint** (R16), con la forma `loyalty.{recurso}.{acción}`. Un endpoint que
  necesita dos permisos son dos endpoints.
* **Nuestras propias superficies consumen solo esta API** (R17). Si la consola necesita algo que la
  API no tiene, la API está incompleta.
* **Errores como RFC 9457** `problem+json` con un `code` estable. El `code` es parte del contrato:
  cambiarlo es un cambio mayor.
* **`Idempotency-Key` donde la tabla lo marca obligatorio.** Un canje repetido por un reintento de
  red no puede cobrar dos veces.
* **Los presupuestos de latencia son criterio de Definition of Done** (R18), verificados con pruebas
  de carga en certificación. No son aspiraciones.

## Cambios y deprecación

Ningún endpoint de esta tabla existe todavía. Cuando el primero se libere, esta página pasa a llevar
también la columna de deprecación: un endpoint se marca deprecado al menos **una versión completa**
antes de retirarse, y el retiro se anuncia en el changelog público.
