# Phase 7.4 — Campaign Engine

| Field | Value |
| --- | --- |
| Identifier | `loyalty-7.4` |
| Status | Complete |
| Module | loyalty |
| Service | `loyalty-service` |
| Version | `0.7.4.0` |
| Database | `loyalty_db` |
| Depends On | Phase 7.3 |
| ADR(s) | ADR-001, ADR-003, ADR-006, ADR-011 |

## Objective

Deliver the Campaign Engine: lifecycle transitions, versioned rules, and apply-to-member point grants exclusively via PointEngine — without referral, wallet, or gift cards.

## Scope

### In Scope

- Lifecycle: schedule / activate / pause / resume / expire
- Rules update with `rule_version` bump
- `CampaignApplication` tracking + idempotency + per-member caps
- Apply grants via `PointEngineService.earn(auto_commit=False)`
- Reject soft-delete while campaign ACTIVE
- Migration `0005_phase_74_campaigns`

### Out of Scope

- Referral (7.5), Wallet (7.6), Gift cards (7.7), Analytics (7.8), Partner APIs (7.9)

## APIs

| Method | Path | Permission |
| --- | --- | --- |
| POST | `/api/v1/campaigns/{id}/schedule` | `loyalty.campaigns.schedule` |
| POST | `/api/v1/campaigns/{id}/activate` | `loyalty.campaigns.activate` |
| POST | `/api/v1/campaigns/{id}/pause` | `loyalty.campaigns.pause` |
| POST | `/api/v1/campaigns/{id}/resume` | `loyalty.campaigns.resume` |
| POST | `/api/v1/campaigns/{id}/expire` | `loyalty.campaigns.expire` |
| POST | `/api/v1/campaigns/{id}/rules` | `loyalty.campaigns.rules.update` |
| POST | `/api/v1/campaigns/{id}/apply` | `loyalty.campaigns.apply` |
| GET | `/api/v1/campaigns/{id}/applications` | `loyalty.campaigns.view` |

## Events

`loyalty.campaign.{scheduled,activated,paused,resumed,expired,rules_updated,applied}`

## Tests

**87 passed** (`pytest -q`)

## Next Phase

Phase 7.5 — Referral Engine

## Related Documents

- [Handover](phase-handover/phase-7-4.md)
- [Service Snapshot](service-snapshots/loyalty.yaml)
