The Churn Problem

Player churn is the single biggest threat to a mobile game's revenue. Studies show that acquiring a new player costs 5-7x more than retaining an existing one. Yet most game studios detect churn only when it is too late -- after a player has already stopped logging in for 14+ days. By then, re-engagement rates drop below 5%.

CCFish needed a proactive approach: detect churn signals early, trigger automated re-engagement campaigns, and measure effectiveness. This post details the serverless churn detection pipeline built on Cloudflare D1 and Workers.

Architecture: Real-Time Churn Scoring

The system uses a multi-signal scoring model that runs every 6 hours via a cron-triggered Cloudflare Worker:

- **Login recency**: days since last session (weight: 40%)

- **Session frequency delta**: sessions per week this month vs. last month (30%)

- **Purchase recency**: days since last IAP (20%)

- **Feature engagement**: number of distinct features used last 7 days (10%)

Each signal contributes to a Churn Risk Score (0-100). Players scoring above 60 enter the watchlist. Those above 80 trigger immediate re-engagement.

Step 1: The Scoring Query

A single D1 query computes the churn risk for all players in under 500ms:

```sql

SELECT

p.id, p.fcm_token, p.email,

ROUND(

CASE

WHEN julianday('now') - julianday(p.last_login) > 7 THEN 40

ELSE (julianday('now') - julianday(p.last_login)) * 5.7

END +

CASE

WHEN s.sessions_last_month > 0 THEN

30 * (1 - CAST(s.sessions_last_week AS REAL) /

GREATEST(s.sessions_last_month / 4.0, 1))

ELSE 30

END +

CASE

WHEN julianday('now') - julianday(p.last_purchase) > 30 THEN 20

ELSE (julianday('now') - julianday(p.last_purchase)) * 0.67

END +

CASE

WHEN COALESCE(f.features_used, 0) < 3 THEN 10

ELSE (3 - COALESCE(f.features_used, 0)) * 3.3

END

) as churn_score

FROM players p

LEFT JOIN session_stats s ON s.player_id = p.id

LEFT JOIN feature_usage f ON f.player_id = p.id

WHERE p.push_opt_out = 0

HAVING churn_score > 60

ORDER BY churn_score DESC;

```

The query runs in a single D1 invocation, returning all at-risk players with their scores and contact tokens. Total execution time averages 320ms for CCFish's player base.

Step 2: Personalized Re-Engagement Campaigns

Once at-risk players are identified, the system triggers personalized campaigns based on churn score and player history:

| Churn Score | Campaign | Channel | Offer |

|---|---|---|---|

| 60-70 | Soft nudge | Push notification | New levels added |

| 70-85 | Incentive | Push + Email | Free gem bundle + login bonus |

| 85+ | Urgent save | Push + Email + SMS | 50% off any IAP + exclusive item |

Each campaign template is stored in D1 and rendered at send time with player-specific variables. The system pulls the right template based on churn score range and fills in player-specific data like name and days since login.

Step 3: Automated Campaign Optimization

The system tracks campaign effectiveness and auto-adjusts:

- Re-engagement rate: percentage of players who return within 7 days

- Cost per saved player: offer cost divided by number of re-engaged players

- Best channel analysis: which channel performs per segment

After each campaign cycle, the Worker updates a D1 optimization table. When a segment-channel-variant reaches 100 samples, the system automatically shifts budget to the highest-performing combination. This continuous optimization loop improves campaign ROI by roughly 15% month-over-month.

Results from Production

Over 12 weeks of automated churn management:

- Monthly churn rate: 18.2% (down from 31% baseline, a 41% reduction)

- Re-engagement success: 34% of at-risk players returned within 7 days

- Revenue recovery: $3,200 per month in salvaged IAP revenue

- False positive rate: 8% (players scored 60+ but were not actually churning)

- Infrastructure cost: $0 (within Workers free tier and D1 5GB storage)

Key Takeaways

- Real-time churn scoring via D1 eliminates the latency gap between signal and response

- Personalization at scale works: targeted offers re-engage 34% of at-risk players

- Serverless architecture keeps costs at zero for indie game volumes

- Multi-channel campaigns outperform single-channel by 2.3x

- Continuous optimization loops improve campaign ROI month-over-month

Data Collection Pipeline

The churn scoring system depends on high-quality behavioral data. CCFish captures player events through a lightweight SDK that sends structured events to a Cloudflare Worker endpoint:

```json

{

"player_id": "abc123",

"event": "session_start",

"timestamp": "2026-05-09T12:00:00Z",

"properties": {

"session_duration": 840,

"level": 12,

"device": "iPhone 15"

}

}

```

Events are batched client-side (every 5 events or 60 seconds, whichever comes first) and sent to the Worker endpoint. The Worker validates the schema, enriches with server-side data (IP geolocation, user-agent parsing), and bulk-inserts into D1 using prepared statements. This batching approach handles up to 10,000 events per second without exceeding D1's write limits.

Handling False Positives

An 8% false positive rate means some players flagged as at-risk are actually still engaged. The system mitigates this through:

- **Confidence bands**: Players scoring 60-65 receive only a soft nudge (no offers) to avoid wasting incentives

- **Cool-off period**: If a player opens the app within 24 hours of a re-engagement campaign, they are removed from the watchlist for 14 days

- **Feedback loop**: Players who return organically after being flagged are analyzed to improve the scoring model

This balance ensures that aggressive re-engagement does not annoy genuinely active players while still capturing nearly all true churn cases.

Scaling Beyond CCFish

The churn detection pipeline is designed as a reusable module. The same Worker code and D1 schema can be deployed for any game or app by changing:

- The event schema mapping (which fields map to churn signals)

- The scoring weights (configurable per-segment via D1 config table)

- The campaign templates (stored in D1, fully customizable)

AIKit's plugin marketplace already offers a packaged version of this pipeline as a drop-in plugin for EmDash-powered game sites. Setup time: approximately 15 minutes from install to first churn report.