The Challenge
Playable ad campaigns generate touches across multiple platforms -- Meta impressions, TikTok views, Google interstitial clicks, and Unity rewarded video completions. Attribution is the nightmare that keeps ad ops teams up at night. A user sees a playable ad on Meta, ignores it, then three days later clicks a TikTok variant of the same campaign and installs. Which channel gets the credit? Without reliable attribution, you are flying blind on budget allocation.
For PlayableAd Studio clients running 10+ campaigns across 4+ networks, the attribution gap means 20-30 percent of marketing spend goes to the wrong channels. A typical campaign with $25,000 monthly budget might misattribute $6,000 worth of conversions -- money that should be reallocated to the highest-performing placements. The problem compounds at scale: a studio running 40 campaigns per month faces $240,000+ in potential misattribution.
The Solution
We built a **Multi-Touch Attribution Engine** that runs entirely on Cloudflare Workers and D1. The engine ingests impression, click, and conversion events from every ad network via webhooks, normalizes them into a unified event stream, and applies configurable attribution models:
- **Last-Click Attribution** (default): Credits the last touchpoint before conversion. Fast, simple, industry-standard baseline. Best when the conversion cycle is under 24 hours and the primary goal is direct response.
- **Linear Attribution** (even split): Divides credit equally across all touches in the 7-day attribution window. Best for brand awareness campaigns where every touch contributes to the final decision.
- **Time-Decay Attribution** (weighted by recency): Touches closer to conversion get more credit. Most accurate for short purchase cycles like hyper-casual game installs where the final click drives the decision.
- **Position-Based Attribution** (U-shaped): 40 percent credit to first touch, 40 percent credit to last touch, 20 percent split among middle touches. Good for campaigns where discovery and conversion are equally important.
Each model is a simple SQL query against the user's touchpoint window. The engine precomputes all four models simultaneously -- the dashboard shows campaign managers the ROAS under each model side by side, so they understand how attribution assumptions affect budget allocation decisions.
Architecture
The engine has three layers:
Ingestion Layer
A Cloudflare Worker exposes a universal webhook endpoint at `events.playableadstudio.com/ingest`. Each ad network sends events here in its native format. A Meta CAPI event has different fields than a TikTok Events API payload, but both get mapped to a unified schema with `user_id`, `event_type`, `timestamp`, `campaign_id`, `creative_id`, and `platform`. The mapping is configurable via KV settings -- adding a new ad network takes a JSON config update, not a code deploy. Failed normalizations (malformed payloads, unknown platforms) go to a Dead Letter Queue in KV for manual inspection. The DLQ is reviewed daily and has caught platform SDK bugs in production.
Attribution Layer
A Durable Object maintains the 7-day touchpoint window per user. Each user gets their own Durable Object (hashed by `user_id`), ensuring sub-millisecond lookups without global database contention. This is the key scalability decision: sharding by user ID avoids the hotspot problem because user-level event traffic is naturally distributed. When a conversion event arrives, the Durable Object retrieves all touches within the window (typically 3-15 events per user) and computes attribution shares for each model simultaneously in under 5 milliseconds.
Reporting Layer
D1 stores aggregated attribution results partitioned by campaign and attribution model. A REST API built on Cloudflare Workers serves dashboard queries. Campaign managers can switch attribution models via a dropdown and see budget reallocation recommendations in real time. The API also exposes an export endpoint for pulling raw attribution data into Looker Studio or Google Sheets for custom analysis.
Results
In beta testing with three PlayableAd Studio pilot clients running a combined 28 campaigns over 6 weeks:
- **70 percent reduction** in time spent on attribution reconciliation (from 8 hours/week to under 2.5 hours)
- **18 percent improvement** in ROAS after switching from default last-click to time-decay model for 60 percent of campaigns
- **100,000+ events processed per day** with zero infrastructure cost at D1's free tier (the Worker free allocation handles ingestion easily)
- Attribution model switch from last-click to time-decay takes under 2 seconds to recompute across all campaigns simultaneously
- Zero data loss during the pilot -- the DLQ captured 47 malformed events from platform SDK bugs that would have been silently dropped without the engine
Implementation Notes
The engine uses Cloudflare Workers' WebSocket support for real-time dashboard updates. When a conversion event triggers an attribution recalculation, the Worker pushes the updated ROAS to all connected dashboard sessions via a single WebSocket broadcast. This gives campaign managers live feedback without polling. The WebSocket connection uses Durable Objects' alarm-based heartbeats to detect stale connections and clean them up every 60 seconds.
Key Takeaways
Multi-touch attribution is solvable at serverless scale without expensive third-party tools like AppsFlyer or Adjust. The key architectural decisions -- per-user Durable Objects for touchpoint storage, configurable model selection via KV settings, and D1 for aggregated reporting -- together deliver enterprise-grade attribution at near-zero marginal cost. For PlayableAd Studio clients, this means every campaign gets attribution analysis that would cost $500-2000/month with standalone tools, included at no extra charge.