DecisaDocs
Back to docs

Docs · Methodology

How Decisa attributes revenue.

Attribution looks simple until you try to defend a number to a board. This page lays out the exact math so you can — including the bits we still get wrong.

01

The canonical chain

Every revenue event in Decisa walks the same chain:

UtmLink  →  Click  →  Session  →  Order  →  AttributedConversion

UtmLink is metadata you own. Click is captured by pixel.js when a visitor lands. Session stitches multiple clicks under one dcs_vid first-party cookie. Order is created when your backend POSTs to /v1/conversions. AttributedConversion is the join row that records which click(s) get credit for which order, under which model, at which point in time.

02

Window and dedupe rules

  • Click → conversion window: 30 days by default. Configurable per workspace.
  • Visitor identity: dcs_vid first-party cookie, 12-month rolling expiry, refreshed on every recognized click.
  • Order dedupe: (workspace_id, external_id) uniqueness. Replays of the same webhook never create a duplicate order.
  • Conversion dedupe (for CAPI pushback): event_id uniqueness per platform + workspace. Each pusher checks the outbound log before sending.

03

Attribution models

Models are first-class entities — switching models does not rewrite history. The previous attribution snapshot stays queryable for reproducibility.

Last-click (default)

100% of order revenue is assigned to the most recent qualifying click within the window. This is what Meta and Google show by default. We start here because it's the easiest number for a finance team to reconcile.

Linear

Revenue is divided equally across every qualifying click in the session. Useful for discovering which top-of-funnel sources are quietly carrying water for the last-click hero.

Position-based (40/20/40)

First click gets 40%, last click gets 40%, middle clicks split the remaining 20% equally. Standard for journeys where introduction and decision-moment both matter.

Time-decay (7-day half-life)

Weight per click = 2^(-age_days / 7), normalized so credits across touchpoints sum to 1. A click 7 days before the conversion carries half the weight of a click on the conversion day. Good for short consideration windows; hides slow-burning brand/PR investments whose impact accumulates over weeks.

04

Versioning

Every AttributedConversion row carries the attribution model name and version it was computed under. If we ever change the math (we will), older rows stay intact and a new version is written alongside. You can pin reports to a model version when defending a number to leadership six months later.

05

What we deliberately do not do

  • We do not fingerprint visitors. No canvas hashing, no font probe, no IP+UA matching. dcs_vid only.
  • We do not store raw emails or full IPs. Emails are SHA256-hashed; IPs are hashed with a per-workspace salt.
  • We do not invent attribution for clicks we never saw. If the pixel wasn't installed when a click happened, that order is marked unattributed. It is never silently reassigned to a different source.
  • We do not match against platform-reported conversions. Our number is independent. We surface the gap; we never paper it over.

06

Known limits

  • iOS in-app browsers aggressively clear cookies. Some clicks lose continuity across app handoffs. We're tracking the gap; we don't yet close it.
  • Cross-device journeys require the user to identify on both devices (e.g. logged-in checkout). Without that, the two sessions stay independent.
  • Server-rendered emails with utm params are not currently joined into the session graph. Coming in a later release.

See the chain in action with a working install? Start at Getting started or read the API reference.