Skip to content

Integration  ·  Marketing

How to build a campaign attribution pipeline

Marketing reports pipeline that finance does not recognise, and both numbers are defensible. Here is the model behind attribution that survives the argument, the prompts that build it, and what it takes to run in production.

Built with Tray Headless

  1. System Marketo
  2. Step Stitch to account
  3. Step Join spend
  4. Step Several models
  5. System Snowflake
Also Looker Salesforce

Several models are published side by side rather than one, because the disagreement between them is the useful part.

The short answer

What is a campaign attribution pipeline?

An attribution pipeline breaks into four parts: stitching touches to the account rather than only the individual, joining spend from the ad platforms so cost per outcome is derived rather than typed, publishing several models side by side instead of one, and reconciling to a number finance recognises. The common failure is picking a single model. First touch and last touch disagree by design, and presenting one as the answer guarantees somebody with a different model calls it wrong.

What matters here

  • Stitch to the account, not only the person. B2B deals involve several people and person-level attribution misses most of the influence.
  • Publish more than one model. First and last touch disagree by design, and the gap between them is information instead of a problem.
  • Join real spend. Cost per opportunity typed into a spreadsheet is an estimate somebody will challenge.
  • Reconcile to a pipeline number finance recognises. An attribution report that cannot be tied back is a report nobody acts on.
  • Keep the raw touches. Every model change is a reprocess, and you will change models.

Who this is for

You run marketing operations or revenue operations. Every quarter attribution is rebuilt in a spreadsheet, and every quarter somebody disputes it with a different model.

How it works in practice

What happens between a click and a defensible pipeline number.

  1. 1

    Touches land raw and keep everything

    Channel, campaign, timestamp, person and account where known. Raw survives every model change.

  2. 2

    Touches are stitched to a person and an account

    Anonymous to known where possible, and person to account, because B2B buying is a committee.

  3. 3

    Spend is joined from the ad platforms

    So cost per outcome is derived from real numbers, not typed into a slide.

  4. 4

    Several models run side by side

    First touch, last touch, linear and a weighted one. Each published, none presented as the answer.

  5. 5

    The result reconciles to CRM pipeline

    Total attributed pipeline ties back to a number finance already accepts.

  6. 6

    The gap between models is reported

    Because where they disagree is where the interesting conversation is.

What attribution is made of

Four parts. The third is what stops the quarterly argument.

Account-level stitching

Touch to person, person to account, including the anonymous-to-known join. B2B deals have five people and person-level attribution sees one.

Joined spend

Real cost from the ad platforms against real outcomes from the CRM. A typed estimate invites a challenge you cannot answer.

Several models

First, last, linear and weighted, published together. One model presented as truth is an invitation for somebody with another to call it wrong.

Reconciliation

Attributed pipeline tying back to CRM pipeline. Without it the report is interesting and not actionable.

The Tray Headless prompts

Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.

Once per project, run /tray-workflows:set-workspace to pick the workspace these build in. Point it at a sandbox first.

  1. 1

    Set up and land the touches raw

    You will change the model, and raw is what makes that cheap.

    Headless skills build-workflow

    Use build-workflow. The systems in play are Marketo, Salesforce and
    Snowflake, or whatever we run in those seats.
    
    Land every touch raw into the warehouse: channel, campaign, medium,
    source, content, timestamp, the person if known, the anonymous id, and
    the landing page.
    
    Do not transform on the way in and do not drop anything that looks
    unused. Every attribution model change is a reprocess of this table, and
    you will change models. Discarding a field now means re-instrumenting
    later.
  2. 2

    Stitch to the person, then to the account

    B2B buying is a committee and person-level attribution sees one member.

    Headless skills build-workflow

    Use build-workflow. Stitch in two stages.
    
    Anonymous to known: when somebody identifies themselves, backfill their
    prior anonymous touches within a sensible window. That backfill is
    usually where first touch actually lives.
    
    Person to account: through the CRM contact, then by email domain, then
    through the account matching rules we already use.
    
    Then roll touches to the account. A deal with five people touched by
    marketing separately is one buying decision, and attributing to
    individuals splits credit across a committee in a way that means
    nothing.
  3. 3

    Join real spend

    A typed cost per opportunity is an estimate somebody will challenge.

    Pull spend from the ad platforms and join it to campaigns on a shared
    identifier, not on a campaign name that somebody typed differently in two
    places.
    
    Where a channel has no platform spend, take committed cost from the
    marketing budget instead of leaving it blank. An events channel with no
    cost attached looks impossibly efficient and discredits the whole report.
    
    Then derive cost per lead, per opportunity and per closed deal. Derived
    from two real numbers, never typed, so when somebody challenges it the
    answer is a query, not a recollection.
  4. 4

    Publish several models, not one

    Where the models disagree is the actual insight.

    Headless skills tray-gotchas

    Use tray-gotchas, then compute several models over the same touch set:
    
      First touch, which flatters awareness channels
      Last touch, which flatters closing channels
      Linear across all touches in the window
      A weighted model with the weights written down and owned by somebody
    
    Publish all of them side by side. Never present one as the answer.
    
    The gap between first and last touch for a channel is the interesting
    number: a channel that looks strong on first and weak on last is doing
    awareness work, and a report that shows only one of those will get it
    defunded or overfunded.
    
    Also handle: touches outside the lookback window, deals with no touches
    at all, which is a stitching failure instead of a sales-sourced deal,
    and self-reported attribution where you collect it.

    Deals with zero touches are the diagnostic worth watching. A rising count almost always means stitching has broken, not that outbound suddenly got better.

  5. 5

    Reconcile to a number finance recognises

    Otherwise it is interesting and nobody acts on it.

    Reconcile every run: total attributed pipeline against total CRM pipeline
    for the period. They will not match exactly, and the difference has to be
    explainable, not hand-waved.
    
    Report the unattributed share explicitly rather than hiding it. A model
    that quietly drops 30% of pipeline is worse than one that shows the gap.
    
    Then report by channel: spend, pipeline and closed under each model,
    cost per opportunity, and the first-versus-last gap. Plus deals with no
    touches, trended, because that is the health of the stitching rather than
    a finding about outbound.
  6. 6

    Validate, then hand the weights over

    Because model weights are a marketing decision, not an engineering one.

    Run the per-step schema checks and the whole-workflow audit before this
    touches production.
    
    Then open the same workflow in Tray Build so marketing operations can
    adjust the lookback windows and the weighted model in the visual canvas.
    Those are marketing decisions and they should be arguable without a
    sprint.

What it connects to

Touches, spend and outcomes live in three places and have to meet in one.

Marketo

Read touches, campaign membership and form fills with their full context.

Reads

Salesforce

Read opportunities and closed outcomes, and write the attributed campaign influence back.

Reads and writes

Google Analytics

Read web sessions and the anonymous-to-known join, which is where first touch usually lives.

Reads

Snowflake

Hold raw touches, spend and every model. This is where attribution actually happens.

Reads and writes

Looker

Publish the models side by side where marketing and finance both look at them.

Writes

Slack

Alert when deals with no touches rise, because that is a stitching failure instead of a finding.

Writes

Same build, other stacks

The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, Google BigQuery, Microsoft Teams, Power BI, HubSpot or Databricks.

Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.

Running it in production

This produces a number two departments argue about. It has to be reconstructable.

It runs on the platform, not on somebody's machine

Touch ingestion, spend joins and model runs execute on the same engine, with the raw layer retained so any model change is a reprocess.

Every number is reconstructable

Which touches, which model version, which spend snapshot. When marketing and finance disagree, that is how it gets settled in an hour.

Credentials are managed, never written into the build

Ad platform credentials expose spend across every campaign. Read-only, in your workspace, and separately rotatable.

Marketing owns the weights

Lookback windows and the weighted model open in Tray Build, because those are marketing judgements that should be arguable without engineering.

The unattributed share is published

A model that quietly drops pipeline is worse than one that shows the gap. Report it every run.

Questions people ask

Why publish several models?

Because first touch and last touch disagree by design. Presenting one as the answer guarantees somebody with a different model calls it wrong, and the gap between them is the actual insight.

Why stitch to the account?

Because B2B buying is a committee. A deal where marketing touched five people separately is one decision, and attributing to individuals splits credit in a way that tells you nothing.

Why join real spend?

Because a cost per opportunity typed into a slide is an estimate, and the first person to challenge it wins. Derived from platform spend and CRM outcomes, the answer is a query.

What does a rising count of zero-touch deals mean?

Almost always that stitching has broken, not that outbound improved. It is the best health check on the pipeline and it is worth alerting on.

Why reconcile to CRM pipeline?

Because an attribution number that cannot be tied back to one finance already accepts is interesting and unusable. Publishing the unattributed share is better than hiding it.

Vibe-coding app guides

Vibe-code an entire app with Helix

This moves the data between systems. It does not give anybody a screen to work in. Build that app in Claude Code, Codex or Cursor, then deploy and run it governed on Tray Helix. Same kind of guide, same kind of prompts.

How to build a marketing budget tracker (opens helix.tray.ai in a new tab)

Last reviewed September 2026.