Multi-touch attribution across channels
The per-campaign ROAS and revenue attribution view answers “how did this one campaign perform”. The multi-touch attribution endpoints answer the broader question a whole marketing program needs: of this month’s conversions, how is the credit split across the channels — email, SMS, push, RCS — and campaigns that touched the converting contacts? Two endpoints compute this on demand over your CDP event stream; neither needs new storage or instrumentation:POST /cdp/analytics/attribution— allocates each conversion’s credit (1.0 per conversion) across its touchpoints and rolls it up by channel and campaign.POST /cdp/analytics/revenue-attribution— allocates each conversion’s monetary value the same way, then pairs each channel/campaign with the spend recorded on its touch events to report ROAS and ROI.
contacts:read scope and an owner/admin/developer role, the same gate as the funnel-analytics read surface.
How the join works
For every conversion event inside the window (since … until, defaulting to the trailing 30 days), the endpoint gathers the subject’s touchpoints in the lookback_days (default 30) before the conversion, and the attribution model allocates credit across them. The subject key is COALESCE(contact_id, anonymous_id, user_id) so anonymous pre-purchase touches stitch to the same conversion they led to. Conversions with zero in-window touchpoints still surface — as an unattributed_conversions counter (and, on the revenue endpoint, unattributed_revenue), never silently dropped from the denominator.
The five attribution models
The model changes how credit splits, not which conversions are counted; totals stay stable across models. The body echoes every figure under the model you asked for, so you can run the same window under two models and compare.
Request fields
Reads are bounded to 50,000 joined touch rows; if the window exceeds that, the response carries
touch_rows_capped: true and the report is a lower bound — the dashboard renders a banner rather than a silently-rounded figure.
Credit report
Revenue + ROAS report
The revenue endpoint extends the credit body with three optional fields —value_property (JSONB property on the conversion event carrying its monetary value, default "value"), cost_property (JSONB property on touch/send events carrying their spend, default "cost"), and a currency label echoed on the response (no FX; all events in one request must share a currency):
total_revenue, attributed_revenue, unattributed_revenue, total_spend, the overall roas / roi_pct / cost_status, and per-channel + per-campaign ROAS rows. A channel or campaign that spent money but earned no attributed revenue still surfaces (ROAS 0), so wasted spend is visible rather than filtered to winners only. Non-numeric value_property rows are guarded — a single dirty row yields 0 on that conversion instead of failing the whole report.
Interpretation notes
- Pick the model that matches how you budget. First-touch credits discovery channels; last-touch credits closers; linear is the neutral per-touch view; time-decay rewards recency; custom lets you weight position deliberately.
- The unattributed counter is a signal, not noise. A large share usually means conversions are arriving from traffic outside your sending channels — or the lookback window is too tight.
- Spend-side of ROAS comes from
cost_property. If your send events don’t stamp a cost, channel ROAS readsno_spendinstead of an infinite ratio.