Skip to content

Product offer engine

ID MOD-108
System SD08
Repo bank-app
Build status Not started
Deployed No

What it does

MOD-108 generates bank-initiated personalised product offers. It operates on the eligible and NBP-ranked product set and derives specific offer terms (interest rate, credit limit, fee structure, promotional bonus) personalised to each customer. Offers are stored with full lifecycle tracking: GENERATED → PRESENTED → ACCEPTED / REJECTED / EXPIRED.

Distinguish clearly from MOD-109 (product deal engine): MOD-108 is systemic and bank-initiated — offers are generated by the engine without a triggering agent interaction. MOD-109 is agent-initiated — a specific deal is proposed by an agent during a customer interaction and requires authorisation.

Why it exists

Personalised offers convert at significantly higher rates than generic rate-card offers. The commercial case (BG-003, BG-005) is conversion improvement through personalisation. The compliance case is that every offer must be traceable — what was offered, to whom, on what basis, and what happened to it — satisfying CON-004 and CON-006.

Offer derivation

Input Source Effect on offer terms
NBP rank MOD-107 Rank 1 product generates the primary offer; ranks 2–3 generate secondary offers
Customer ROTE MOD-106 High-ROTE customers eligible for premium rates; low-ROTE customers offered standard terms
Behavioural consent MOD-049 If marketing consent present: personalised rate. If not: standard rate-card offer only
Pre-approval limit MOD-029 For credit products: offer limit derived from pre-approval result; never exceeds affordability max
Product rate card Product configuration Floor and ceiling for offer rates; offer cannot exceed ceiling or fall below floor

Financial advice licensing gate

Before generating an offer for any product flagged requires_advice_review = true in the product register, MOD-108 checks that an authorised adviser has reviewed the customer profile within the past 12 months (read from product_eligibility.advice_reviews). If no review exists, the offer is suppressed and an offer.advice_review_required event is raised for the back-office queue. Currently no products in the bank's register require this gate, but the gate is built and active — it will be triggered when investment or KiwiSaver distribution products are added.

Offer lifecycle

GENERATED
  → PRESENTED (displayed in app or surfaced to agent)
    → ACCEPTED  (customer accepted offer terms; triggers application workflow)
    → REJECTED  (customer declined)
    → EXPIRED   (offer validity period elapsed — default 30 days for credit; 14 days for rate offers)
  → SUPPRESSED (advice review required; or eligibility re-evaluation failed before presentation)

Data model

-- app.product_offers (Postgres — bank_app)
CREATE TABLE app.product_offers (
  offer_id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  party_id            uuid NOT NULL,
  product_id          text NOT NULL,
  jurisdiction        text NOT NULL CHECK (jurisdiction IN ('NZ','AU')),
  offer_terms         jsonb NOT NULL,         -- rate, limit, fee waiver, promotional bonus
  derivation_basis    jsonb NOT NULL,         -- snapshot of inputs used to derive terms
  status              text NOT NULL CHECK (status IN ('GENERATED','PRESENTED','ACCEPTED','REJECTED','EXPIRED','SUPPRESSED')),
  nbp_rank            int,
  offer_type          text NOT NULL CHECK (offer_type IN ('RATE','LIMIT','FEE_WAIVER','PROMOTIONAL')),
  valid_from          timestamptz NOT NULL DEFAULT now(),
  valid_to            timestamptz NOT NULL,
  presented_at        timestamptz,
  responded_at        timestamptz,
  response            text,                   -- ACCEPTED / REJECTED / null
  created_at          timestamptz NOT NULL DEFAULT now()
  -- append-only: no UPDATE/DELETE; status changes are new rows via offer_events
);

-- app.offer_events (append-only lifecycle log)
CREATE TABLE app.offer_events (
  event_id    uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  offer_id    uuid NOT NULL REFERENCES app.product_offers(offer_id),
  event_type  text NOT NULL,   -- GENERATED, PRESENTED, ACCEPTED, REJECTED, EXPIRED, SUPPRESSED
  actor       text NOT NULL,   -- 'system' or staff_id
  event_at    timestamptz NOT NULL DEFAULT now(),
  detail      jsonb
);

Events

  • product.offer_presented — on every app presentation; carries party_id, product_id, offer_id, jurisdiction.
  • product.offer_accepted — triggers application or account-open workflow in the relevant system domain.
  • product.offer_expired — nightly sweep; triggers NBP re-evaluation for the customer.

Module dependencies

Depends on

Module Title Required? Contract Reason
MOD-105 Product eligibility engine Required Eligibility gate — MOD-108 cannot generate an offer for a product that MOD-105 has not confirmed eligible.
MOD-107 Next best product engine Required NBP rankings from MOD-107 determine which eligible products progress to offer generation.
MOD-049 Open banking consent management Required Consent record for data-driven marketing must be present before behavioural offer terms are derived.
MOD-104 AWS shared infrastructure bootstrap Required AWS shared infrastructure provisioned by MOD-104 is required before this module can be deployed.
MOD-103 Neon database platform bootstrap Required Neon database provisioned by MOD-103 must exist before this module can read or write Postgres.

Required by

(No modules in this wiki currently declare a dependency on this module.)


Policies satisfied

Policy Title Mode How
CON-006 Product suitability and governance GATE No offer is generated for a product that is not in the customer's eligible set from MOD-105 — eligibility check is a hard pre-condition for offer generation.
CON-004 Product Disclosure & Sales Practice Policy LOG Every generated offer is logged with its terms, derivation basis, and lifecycle events (presented, accepted, rejected, expired) in an immutable offer audit trail.
CON-001 Customer Fairness & Conduct Policy AUTO Offer generation rate and acceptance outcomes are monitored by MOD-107 fairness reporting — systematic disparities trigger a compliance alert.
PRI-001 Privacy Policy GATE Behavioural personalisation of offer terms requires the customer's active consent record for data-driven marketing — no behavioural offer is generated without a valid consent.

Capabilities satisfied

(No capabilities mapped)


Part of SD08 — Customer App & Back Office Platform Compiled 2026-05-22 from source/entities/modules/MOD-108.yaml