> ## 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

> Softcrum Loyalty is a headless engine for customer loyalty and incentives. A company connects its own systems — point of sale, ecommerce, ERP, billing — and behavioural events start flowing in.

> Softcrum Loyalty is a headless engine for customer loyalty and incentives. A company connects
> its own systems — point of sale, ecommerce, ERP, billing — and behavioural events start flowing
> in. From those events the engine awards points, issues coupons, moves customers between tiers,
> pays out referrals and triggers campaigns, all from rules the company configures itself. It runs
> either inside our own console and member portal, or entirely embedded in the company's own
> experience through the public API. The engine is the product; the interface is a choice.

## Who this is for

| Persona                              | Hires this module to                                                                                                                                                       |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Marketing lead** (tenant)          | Launch a loyalty program without engineering: define how points are earned, what they buy, which tiers exist, and what gets sent when.                                     |
| **Developer** (tenant or integrator) | Connect an existing system in an afternoon: send events, read balances, redeem at the point of sale — with idempotency guarantees that survive retries and flaky networks. |
| **Finance** (tenant)                 | Know the outstanding points liability, refreshed often enough to close a month with confidence.                                                                            |
| **Member** (the tenant's customer)   | See a balance they trust, understand what it takes to reach the next tier, and redeem without friction or an app download.                                                 |

## The problem today

Companies that want a loyalty program choose between two bad options. Packaged ecommerce loyalty
apps are fast to install but lock the experience into a widget and a vertical — they cannot model
a subscription business's payment behaviour or a services company's anniversaries. Enterprise
engines are genuinely flexible but are enterprise purchases: long sales cycles, opaque pricing,
and integration projects rather than an afternoon.

Underneath both sits a pricing wound the market talks about constantly. The dominant engagement
platforms bill for every profile stored whether or not it is contacted, and at least one applies
a ratchet that keeps billing a peak long after usage falls. The result is that importing your own
history — the single thing that makes a loyalty program work on day one — is financially
punished. Being the platform that does not do that is a positioning decision, not a discount.

## What it does *(normative)*

* A tenant defines a **program**: its point currencies, its earning rules, its reward catalog,
  its tiers.
* Behavioural events award points through a **rules engine** whose contract is
  event → conditions → effects, with versioned campaigns and budgets.
* Points live in an **immutable ledger** with two separate currencies — redeemable points and
  status points — and FIFO lots with real expiration. Balances are a projection, never a mutable
  column.
* Points and coupons are **redeemed** through an idempotent API a point of sale can call safely,
  with parent/child redemptions and an explicit rollback policy.
* **Referrals** pay both sides, released by a qualifying event rather than by signup, with
  anti-fraud controls.
* **Tiers** qualify over a defined window, with an explicit downgrade policy and per-tier
  overrides on earning, pricing and expiration.
* Everything emits domain events that the messaging module turns into campaigns, and that a
  tenant can consume as outgoing webhooks.

## Non-goals *(normative)*

* **Not a promotions engine in F1.** Dynamic cart discounts (`apply_discount`) are ratified for
  F2 (DEC-H4); F1 leaves the effect catalog open so adding it costs nothing.
* **Not a payments system.** Mixed points-plus-money redemption is a confirmed requirement
  (DEC-H3), but F1 only reserves the seam — a nullable `money_component` and a domain event.
* **Not a CDP.** Profiles, identities, consents, events and segments belong to `customer-core`.
  Loyalty is a consumer of that core, never an owner of it.
* **No cross-program aggregation.** Multi-program ships in F1 (ADR-021), but each program reports
  on itself. Aggregating across programs raises questions — whose points, which currency, which
  tier ladder — that get their own decision when a customer actually needs it.
* **Not gamification in F1.** Badges and challenges have reserved schema and land in F2.
* **No vertical hardcoding.** Event taxonomies ship as installable data, not code (DEC-H7). The
  product must apply to any industry.

## Success

| Measure                                                                                                              | Target                                                     | By                        |
| -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------- |
| Design partner running the full loop — event in, points awarded, notification delivered, redemption at point of sale | operating in production                                    | G1-Engage, 2026-11-01     |
| Time from API key to first event acknowledged                                                                        | under 30 minutes, unaided, following the public quickstart | G1-Engage                 |
| `track` p95                                                                                                          | \<100 ms (fast-ack)                                        | certification, pre-G1     |
| End-to-end effect visibility (event → points → notification)                                                         | \<5 s p95                                                  | certification, pre-G1     |
| Ledger drift found by nightly reconciliation                                                                         | zero, sustained                                            | first month in production |

## Commercial shape

The billable base is the **marketable contact** — a contact with active consent on at least one
channel and not suppressed (ADR-014). Stored history is free and unlimited: importing years of
customers costs nothing, which is the adoption lever. There is no ratchet; the count re-evaluates
each cycle in both directions.

Loyalty itself meters on volume: tracked events and messages, with published unit prices and
allowances per plan (ADR-016). Two capabilities are sold rather than included. The **data
freshness** add-on raises the frequency of heavy recomputes — drift reconciliation, full RFM,
analytical snapshots — from nightly to as often as hourly (DEC-G5). And **program count is an
entitlement** (ADR-021): the base plan includes a number of programs, and needing another is a
concrete reason to move up a tier. Exceeding it is blocked, never billed as overage — a hard limit
is the right shape for a capability the tenant chooses to adopt, unlike consumption their own
customers drive.

`contact_balances` is never part of that add-on. A member sees their balance the instant it
changes; charging for that would be charging for correctness.

## Phases

| Phase   | Contents                                                                                                                                                       | Target                |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| **F1a** | Programs and currencies (multi-program, ADR-021) · ledger and FIFO lots · balance projection and reconciliation · rules engine · rewards catalog · redemptions | G1-Engage, 2026-11-01 |
| **F1b** | Coupons and mass code generation · stacking rules · referrals · tiers · expiring-points scans · console UX for rules and tiers                                 | post-G1               |
| **F2**  | Badges and challenges · `apply_discount` · wallet passes · mixed points + money redemption                                                                     | 2027                  |

## Compliance and risk

Loyalty stores behavioural and transactional data about identified people, which is exactly what
profiling regulation targets. Two consequences are already fixed and constrain the design:

* **Erasure is asymmetric.** A data subject exercising deletion has their profile and events
  deleted, but ledger rows are **anonymized to a tombstone, never destroyed** (DEC-J3) —
  accounting integrity and the right to erasure are reconciled by anonymization, not by deletion.
  Committed execution is ≤30 days, automated.
* **The ledger is the evidence.** Ley 21.719 audits operational evidence rather than policies.
  The append-only ledger plus the audit log is what we would show.

Main product risk: a rules engine expressive enough to be useful and restricted enough to stay
predictable. The mitigation is that effects are a parametric catalog — adding a row does not add
behaviour, and the API rejects codes the business does not yet support.

## Dependencies

`customer-core` for contacts, identities, consents, events and segments — a hard dependency, and
one-way. `messaging` for every notification, through domain events only. `QueuePort` for
asynchronous effects, `NotificationChannelPort` for delivery. Postgres partitioning for the
event stream feeding the rules engine.

## Open questions

None outstanding. Every question this spec carried was answered in the consolidated register
([`../../../design/open-questions-v1.md`](https://github.com/softcrumlabs/softcrum-suite/blob/master/docs/design/open-questions-v1.md), v1.1) and folded
into the normative sections above.

## Changelog

| Version | Date       | Change                                                                     | Why                                      | Author                 |
| ------- | ---------- | -------------------------------------------------------------------------- | ---------------------------------------- | ---------------------- |
| 0.2.0   | 2026-08-17 | Open questions resolved (OQ-LOY-\*) and folded into the normative sections | Owner answered the consolidated register | daniel + claude-opus-5 |
| 0.1.0   | 2026-08-17 | Initial draft from the decision registry and module spec                   | —                                        | daniel + claude-opus-5 |
