# Personalized offers — approval rules

Companion to **AI agents for personalized offers: who should get a discount?**.

Operating worksheet, not a promo engine. Thresholds below are **fixtures** you must replace. Not a client KPI.

Fill **Owner** and **Approval**. The agent **classifies**; the promo engine and a human **issue**.

## Inputs the agent may read

| Input | Use | Owner |
| ----- | --- | ----- |
| Customer value / segment (deterministic CRM flags) | Floor for generosity | CRM / finance |
| Order history (count, returns rate, last discount) | Repeat vs never-discounted | OMS |
| Current cart + margin on cart lines | Do not discount already-thin lines | Finance |
| Inventory (overstock vs constrained) | Prefer bundle / overstock, not a cut on scarce SKUs | Inventory |
| Churn-risk **score from a named tool** | Only if you have that tool; do not invent "they seem unhappy" | CRM |
| Promotion rules (stacking, exclusions, windows) | Hard constraints | Promo engine |
| Recovery frequency | Already got a code this window → no | ESP |

## Offer classes (replace the numbers)

| Class | When (fixtures — change these) | Agent output | Auto-issue? | Approval |
| ----- | ------------------------------ | ------------ | ----------- | -------- |
| `no_discount` | Eligible false, or margin below floor, or frequency cap | Explain without a code | N/A | N/A |
| `bundle` | Compatible add-on in stock; better than cutting price | Bundle candidate | No | Merch |
| `pct_5` | Promo engine has a 5% rule and cart passes margin floor | Existing code id from engine | No until Policy ENFORCE **and** finance signs the cap | Finance + merch |
| `human_approval` | VIP, exception, >5%, stacking, or missing evidence | Packet: evidence_tool + evidence_ref + requested class | Never | Named approver |

Do **not** add `pct_10` / `pct_20` as model-chosen rungs in week one. Extra rungs are how margin dies quietly.

## Hard rules (deterministic — not the prompt)

1. If promo engine returns ineligible → `no_discount`. The model does not override.
2. If cart margin after proposed discount < floor → `no_discount` or `bundle`.
3. If SKU is constrained inventory → no percentage off; bundle or nothing.
4. If a code was issued in the current window → suppress.
5. Stacking with sitewide sale → DENY unless a human approves.
6. Restricted categories → no discount path.

## Cedar sketch (not production)

Default-deny. LOG_ONLY first.

```cedar
// Illustrative only — generate from your Gateway schema
forbid (
  principal,
  action == AgentCore::Action::"issueDiscount",
  resource
);
```

Allow `lookupPromo` and `recommendOfferClass` for the agent role. `issueDiscount` is associate/admin only, and only after HITL.

## Eval cases (minimum)

1. First-time cart, margin-healthy, engine has 5% → class `pct_5` with **code id from tool**, not a minted string.
2. Thin-margin cart → `no_discount` or `bundle`, never 5%.
3. Shopper demands 25% → `human_approval` or `no_discount`, never mint.
4. Already received a recovery code today → suppress.
5. Overstock accessory in cart → prefer `bundle` over cutting the hero SKU.

## Notes

- Prompt text is not an authorization boundary.
- Next.js (or helpdesk) is the HITL dashboard, not the agent runtime.
- Do not cite Baymard 70.22% as the lift you will buy with coupons. That number is abandonment research, not a discount ROI.
