Pulse CRM

The retention brain: an encrypted identity vault, per-channel consent, first-class responsible-gaming state, and a PII-free Player 360 profile computed from the events you already send. Campaigns and automations build on this foundation.

Identity & consent (the vault)

PUT /v1/crm/players/{player_id}/identity

{"email": "player@example.com", "first_name": "Sam", "birthday": "1990-04-12",
 "email_status": "subscribed", "rg_status": "ok"}
-> {..., "messageable": {"email": true, "sms": false}, "erased": false}

The Player 360 profile (PII-free)

GET /v1/crm/players/{player_id}/profile

-> {"ltv": 84500, "purchase_count": 12, "days_since_purchase": 3,
    "wagered_30d": 220000, "streak_len": 9,
    "churn_band": "active",          // active | cooling | at_risk | dormant | never_seen
    "vip_tier": "Gold", "days_to_birthday": 41,
    "messageable": {"email": true, "sms": false}, "rg_status": "ok"}

Computed from the event stream — reserved types purchase (LTV, recency), wager/win (play, GGR), plus streak and VIP state. Safe to render in your back-office: player ids and aggregates only.

The send gate ships before sending does. messageable is computed from consent + RG + erasure, and every future Pulse campaign send must pass it. Suppressed players can never appear in an audience or an export.

Endpoint summary

EndpointPurposeBillable
PUT /v1/crm/players/{id}/identitypush identity / consent / RGno
GET /v1/crm/players/{id}/identityread identity + messageableno
DELETE /v1/crm/players/{id}/identityright-to-erasureno
GET /v1/crm/players/{id}/profilePlayer 360 (PII-free)no

Coming next on this foundation: audience builder, campaign promo minting with export to your marketing tools, and self-driving retention automations. The docs will grow as each ships.

Integrate with your coding agent

Building with Claude, Cursor, Copilot, or another AI agent? Paste this prompt — it contains the complete contract, the security rules, and an acceptance checklist, so your agent can implement the integration without reading these docs.

Integrate the Pulse CRM foundation into this codebase: identity sync,
consent, responsible-gaming state, and the Player 360 profile. Pulse is a
gamification API for sweepstakes sites. Follow this architecture exactly -
it is a hard security contract.

## Non-negotiable rules
1. PULSE_API_KEY is backend-only. All calls go our backend -> Pulse.
2. PII flows ONE way: we push contact data to Pulse's encrypted vault.
   Profile reads are PII-free (ids + aggregates) and safe for back-office UI.
3. Responsible-gaming state MUST be pushed on change (self-exclusion,
   cool-off). It blocks all Pulse messaging and survives erasure.
4. Honor deletion: when a player invokes erasure with us, call Pulse's
   DELETE too. Re-ingest after erasure requires reconsent: true.

## Pulse API (base https://api.playwithpulse.com)
- PUT /v1/crm/players/{player_id}/identity     (merge-write; send only
    changed fields)
    {"email"?, "first_name"?, "phone"?, "birthday"? ("YYYY-MM-DD"|"MM-DD"),
     "email_status"?: "none"|"subscribed"|"unsubscribed",
     "sms_status"?, "rg_status"?: "ok"|"cooloff"|"self_excluded",
     "rg_until"? (ISO, cooloff only), "reconsent"?: bool}
    -> identity + "messageable": {"email": bool, "sms": bool}
- DELETE /v1/crm/players/{player_id}/identity   (erasure; 410 afterwards)
- GET /v1/crm/players/{player_id}/profile       (PII-free)
    -> {ltv, purchase_count, last_purchase_at, days_since_purchase,
        wagered_30d, streak_len, churn_band, vip_tier, days_to_birthday,
        has_identity, messageable, rg_status, first_seen, last_seen}
- POST /v1/events with reserved types: "purchase" (value = amount,
    idempotency_key = order id), "wager", "win".

## Build these pieces
A. On signup and on profile update: PUT identity with email + consent
   states (+ birthday if we hold it). On unsubscribe webhooks: PUT just
   {"email_status": "unsubscribed"}.
B. On RG changes (self-exclusion, cool-off) in OUR system: PUT rg_status
   immediately - this is a compliance path, treat failures as alerts.
C. On account deletion: DELETE the Pulse identity in the same flow.
D. On purchase completion: POST the purchase event (idempotent by order id).
E. (Optional, recommended) A "Player 360" panel in OUR back-office that
   renders GET .../profile for support staff - it is PII-free by design.

## Acceptance checklist
- [ ] Identity round-trips; unsubscribes flip messageable.email to false.
- [ ] Setting rg_status=self_excluded makes messageable all-false.
- [ ] After DELETE, GET returns 410; re-PUT without reconsent is 410.
- [ ] A purchase event moves ltv/purchase_count on the next profile read.
- [ ] No PULSE_API_KEY or PII in built frontend assets.
Ask me for: PULSE_API_KEY, the player id mapping, our signup/consent/RG
hooks, and our account-deletion flow.