Skip to main content

Contact Attribution

Attribution answers the two questions every growing team asks: "Where did this contact come from?" and "Which channels are actually working?"

Expedify records this automatically for every contact, no matter how it was created — a website form, a WhatsApp message, a phone call, a Meta lead ad, a CSV import, or an API call. No contact is ever left unattributed: when a channel can't provide marketing details (for example, a manual entry), the contact is classified under Offline Sources rather than left blank.

Two facts, never confused

Attribution tracks two independent facts about every contact. Keeping them separate is the whole idea — collapse them into one and you always lose information.

  1. How the record entered your CRM — the mechanism. A form, a WhatsApp message, an import, the API. This is the Record Source, and it never changes once set.
  2. Where the person came from, marketing-wise — the origin. Google Ads, an Instagram post, a referral, direct. This is the Source / Medium / Channel, and it evolves as the contact interacts with you.

These are genuinely different. A contact can be created via WhatsApp yet be attributed to Paid Social — because they tapped a click-to-WhatsApp ad on Instagram before ever messaging you. Record Source = whatsapp; Channel = Paid Social. If Expedify only stored one of these, you could answer "how many leads did WhatsApp create?" or "which ad channel drove them?" — never both.

The one-line summary

Record Source = which door they walked through. Source/Channel = what made them walk toward it.

The field groups at a glance

Every contact carries four groups of fields. They look similar but answer different questions, and they update under different rules:

GroupFieldsAnswersWhen it changes
Record sourceRecord Source, Record Source Detail"How was this record created, and by which specific form / integration / key?"Written once at creation. Never changes
UTM (raw)UTM Source, UTM Medium, UTM Campaign, UTM Term, UTM Content"What exact tracking parameters were in the URL they clicked?"Only when the contact visits your site through a UTM-tagged link. Never invented
Acquisition (first touch)Acquisition Channel, Source, Medium, Campaign, Date, Entry/Exit Landing Page"How did this person originally find you?"Set once, at the first known interaction, then protected
Latest touchSource, Medium, Channel"What was their most recent interaction?"Updates every time the contact interacts through a new channel or campaign

The rest of this page walks through each group with real examples, then shows a full customer journey where you can watch the fields fill in over time.

A full customer journey

The clearest way to understand attribution is to follow one person from first click to won deal. Meet Priya.

January — she clicks a Google ad. Priya searches "best CRM for agencies," clicks your Google ad, and lands on yoursite.com/pricing?utm_source=google&utm_medium=cpc&utm_campaign=q1_brand&gclid=abc123. She browses but doesn't convert. The tracking pixel records her as an anonymous visitor.

February — she fills a form. Priya returns (this time by typing your URL directly), reads a case study, and submits your "Book a Demo" form. The moment she submits, her anonymous history links to the new contact:

FieldValueWhy
Record Sourceweb_formThe form created the record
Record Source DetailBook a Demo (form)The specific form
Acquisition ChannelPaid SearchHer first touch was the Google ad
Acquisition Source / Mediumgoogle / cpcFrom that first tagged visit
Acquisition Campaignq1_brandThe ad campaign
Acquisition Entry Landing Page.../pricing?...First page of her first session
UTM Source / Medium / Campaigngoogle / cpc / q1_brandThe verbatim tags she arrived with
Latest Source / Channelgoogle / Paid SearchHer most recent touch so far

Notice the direct February visit did not overwrite her Google origin — more on that below.

March — she messages you on WhatsApp. Priya has a question and messages your business number. Her latest-touch fields move; her acquisition fields don't:

FieldBeforeAfter
Acquisition ChannelPaid SearchPaid Search (unchanged — this is how you won her)
Latest ChannelPaid SearchWhatsApp
Latest Sourcegooglewhatsapp

April — a deal is created. Your rep opens a deal for Priya. The deal inherits her acquisition attribution automatically:

Deal fieldValue
Acquisition ChannelPaid Search
Acquisition Sourcegoogle
Acquisition Campaignq1_brand

So when finance later asks "which channel produced this revenue?", the answer — Paid Search — is already on the deal. Priya's story stays intact end to end: created by a form, acquired by Paid Search, last seen on WhatsApp, revenue credited to Paid Search.

Record source (how the record was created)

Two fields capture the creation mechanism. They are written once and never change.

FieldWhat it holdsExample
Record SourceThe mechanism, from a fixed listweb_form, whatsapp, meta_lead_ads, voice, api, import, crm_ui, chatbot, signup, callyzer
Record Source DetailThe specific instance of that sourceThe form, the WhatsApp number/integration, the API key name, the team member, the ad integration

Record Source Detail is an identifier, not always a friendly name. For an integration-created contact it points to the specific integration; for a form it points to the specific form; for an API contact it's the API key name; for a voice contact it's the number that was called. This lets multi-number, multi-form teams segment precisely — for example, "contacts created by our Mumbai WhatsApp line" or "leads from the pricing-page form."

Why store the mechanism separately from the marketing origin? Because they answer different business questions:

  • "How many leads did our forms capture last month?" → group by Record Source
  • "Which marketing channel drives the most leads?" → group by Acquisition Channel

UTM fields (raw URL parameters)

These hold the verbatim utm_* parameters from the most recent tagged visit to your website. They come only from real URLs captured by the tracking pixel or tagged campaign links — they are the raw evidence, never a calculated value.

FieldExampleNotes
UTM Sourcegoogle, newsletterWhich site or property sent the visitor
UTM Mediumcpc, emailThe marketing medium of the link
UTM Campaignsummer_saleCampaign name from the URL
UTM Termcrm softwarePaid-search keyword (if passed)
UTM Contentbanner_aAd/creative variant (if passed)

Empty UTM fields are often correct. A contact who arrived via WhatsApp, a phone call, or an import never clicked a tagged URL — so there is nothing to record. Their acquisition story lives in the Acquisition fields instead.

Don't UTM-tag your own internal links

UTM parameters are for external campaign links only — the links in your ads, emails, and social posts. If your own website's navigation links carry utm_source=website, every internal click overwrites the visitor's real origin with "website," and you lose the fact that they actually came from a Google ad. Expedify defends against this (a self-referential "website" tag is treated as Direct and cannot overwrite a real marketing source), but the cleanest data comes from never tagging internal links in the first place.

UTM vs Source: why both exist

A common question: if the URL already has utm_source=google, why is there also a separate Source field? Because UTMs are the raw input and Source is the normalized, always-present output:

  • UTMs are only present when someone clicked a tagged link. A WhatsApp lead has no UTMs — but still needs a Source (whatsapp).
  • UTMs are verbatim and messy (utm_source=Google_Ads, utm_source=google, utm_source=goog). Source normalizes these to one clean value (google) so your reports don't fragment.
  • Source can be derived even without UTMs — from the referring site (arriving from google.com with no tags → google / organic).

So UTMs feed Source when present; Source always has a sensible value regardless.

Acquisition fields (first touch — set once)

FieldWhat it holdsExample
Acquisition ChannelThe classified marketing channel of their first known interaction — see channel listPaid Search
Acquisition SourceNormalized origingoogle, facebook, whatsapp, import
Acquisition MediumNormalized mediumorganic, cpc, messaging, offline
Acquisition CampaignCampaign that acquired themq1_brand (ad campaign, or your Expedify campaign)
Acquisition DateTimestamp of the first known interaction (UTC)2026-01-14 09:22 UTC
Acquisition Entry Landing PageFirst page of their first website session.../pricing?utm_source=google...
Acquisition Exit Landing PageLast page of that same first session.../book-a-demo

These are written once and then protected. Two protection rules matter:

  • Imports and API calls never overwrite an existing acquisition record.
  • Explicitly supplied values are locked. If you import contacts with their own acquisition data (say, from a previous CRM), those values are marked as explicit and will never be replaced by automatic attribution.

There is one deliberate exception — the true first touch upgrade: if a contact was created through an offline channel (say, a WhatsApp message) and Expedify later discovers earlier anonymous website visits by the same person, the acquisition fields upgrade to that earlier, truer first touch — for example, the Google ad they clicked before ever messaging you. Locked (explicitly imported) values are never upgraded.

Landing pages need a website visit

Entry and Exit landing pages are only filled for contacts with website history. A contact created purely from WhatsApp, a phone call, or an import has no page to record, so these stay empty — until (and unless) that person is later matched to real website visits.

Latest-touch fields (always current)

FieldWhat it holds
SourceNormalized source of the most recent interaction
MediumNormalized medium of the most recent interaction
ChannelClassified channel of the most recent interaction

Latest-touch updates when the contact does something new: visits your site in a new session, sends a WhatsApp message, calls in, or clicks a campaign link.

Two refinements worth knowing:

  • Direct visits don't erase a known source. If their last attributed touch was Paid Search, a later direct visit (typed URL, bookmark) keeps Paid Search — so your reports don't dissolve into "Direct" over time. The latest-touch timestamp still updates.
  • Rapid repeat touches are de-duplicated. Refreshing a page or submitting the same form twice in quick succession won't spam the timeline with identical touches.

How every contact gets attributed

This is the "what marks what" — how each way a contact can enter Expedify translates into attribution. The Record Source column is the creation mechanism; the Channel / Source / Medium columns are the marketing origin.

How the contact arrivedRecord SourceAcquisition ChannelSource / Medium
Website form/chat, after clicking a tagged linkweb_form / chatbotClassified from the UTMs (e.g. Paid Search, Email)The UTM values
Website form/chat, arriving from a search engineweb_form / chatbotOrganic Searchgoogle (etc.) / organic
Website form/chat, arriving from social mediaweb_form / chatbotOrganic Socialfacebook (etc.) / social
Website form/chat, from another websiteweb_form / chatbotReferralreferring domain / referral
Website form/chat, no referrerweb_form / chatbotDirectdirect / none
WhatsApp message from a click-to-WhatsApp adwhatsappPaid Socialfacebook / paid_social
WhatsApp message (organic or campaign reply)whatsappWhatsAppwhatsapp / messaging
Inbound phone callvoiceVoicecall / voice
Meta (Facebook/Instagram) lead formmeta_lead_adsPaid Socialfacebook / paid_social
Email or SMS campaign click (return visit)(unchanged)(updates latest touch) Email / SMSemail / sms
CSV import — without attribution columnsimportOffline Sourcesimport / offline
CSV import — with your own attribution columnsimportClassified from your values, then lockedYour values
Created manually in the CRMcrm_uiOffline Sourcescrm_ui / offline
Created via the APIapiOffline Sources (or classified, if the call includes attribution)api / offline
Created by a workflowworkflowOffline Sourcesworkflow / offline
New account signup (self-serve)signupClassified from signup UTMs, else Directsignup UTMs, else direct

Worked example: WhatsApp, three ways

WhatsApp is the best illustration of why Record Source and Channel are separate, because the same record source produces different channels:

SituationRecord SourceChannelSource / MediumAlso captured
Person taps your click-to-WhatsApp ad on InstagramwhatsappPaid Socialfacebook / paid_socialThe ad's click ID + ad ID
Person messages your number organicallywhatsappWhatsAppwhatsapp / messagingWhich WhatsApp number received it
You import your WhatsApp contact bookintegrationOffline Sourceswhatsapp_business_sync / integration

All three are "WhatsApp" to a human, but the attribution correctly separates a paid-ad acquisition from an organic chat from a bulk sync.

Channel definitions

Acquisition Channel and the latest-touch Channel always come from a fixed list, so your reports stay consistent:

ChannelMeaning
Paid SearchPaid ads on search engines (Google Ads, Bing Ads)
Paid SocialPaid ads on social platforms (Meta, LinkedIn, …) — including click-to-WhatsApp ads
Paid Shopping / Paid Video / Display / Cross-networkOther paid formats
Organic SearchUnpaid search-engine traffic
Organic Social / Organic Video / Organic ShoppingUnpaid platform traffic
EmailEmail links (including your Expedify email campaigns)
SMSSMS links
WhatsAppWhatsApp conversations (organic or your campaigns)
VoiceInbound phone calls
ReferralLinks from other websites
Affiliates / Audio / Mobile Push NotificationsSpecialty sources
DirectTyped URL or bookmark, no known origin
Offline SourcesEverything that doesn't come from tracked marketing: imports, manual entry, API, integrations
UnassignedCouldn't be classified (rare)

Deals inherit attribution

When a deal is linked to contacts, it inherits the acquisition channel, source and campaign of its primary contact (or the first linked contact if none is marked primary). That makes revenue-by-channel reporting a single grouping — every won deal already knows which channel originally produced its contact. Inheritance happens once and is not overwritten, so the deal's origin stays stable even as contacts are added or reordered.

FAQ

What's the difference between Record Source and Source? Record Source is how the record was created (form, WhatsApp, import, API) — it never changes. Source is where the person came from, marketing-wise (google, facebook, direct) — it updates with their latest interaction. A contact created via a form (Record Source = web_form) can have Source = google because they arrived through a Google ad. See Two facts, never confused.

Why are the UTM fields empty on my contact? Because that contact never clicked a UTM-tagged link. UTM fields record real URL parameters only — they are never fabricated. Check the Acquisition fields for how the contact was actually acquired.

Why does Acquisition Channel say "Offline Sources"? The contact entered Expedify through a path that carries no marketing signal — an import, manual entry, API call, or integration. If the same person is later identified on your website with earlier visit history, the true-first-touch upgrade can replace it with their real marketing origin.

Why is Acquisition Channel "Direct" for a contact that came from our website? "Direct" means we couldn't see a marketing origin — they typed your URL, used a bookmark, or arrived through an internal link tagged utm_source=website. It's the honest answer when no external source is known.

Can I set attribution myself? Yes — include acquisition fields in your CSV import or API call. Values you supply explicitly are honored, classified, and then locked so automatic attribution never overwrites them.

Why didn't Latest Source change when the contact visited our site directly? By design: a direct visit doesn't erase a known marketing source. The latest-touch timestamp still updates.

Which of our WhatsApp numbers did this contact come through? Record Source Detail records the specific integration (number) that received the first message, so multi-number teams can segment by line.

A WhatsApp contact shows Channel = Paid Social. Is that a bug? No — that's the system working correctly. They started the chat by tapping a click-to-WhatsApp ad, so the record was created via WhatsApp but the person was acquired through a paid social ad.

What timezone are the attribution dates in? All attribution timestamps are stored in UTC and displayed using your organization's timezone settings.

What about visitors who haven't become contacts yet? Anonymous visitor activity is tracked separately — see Visitor Tracking. The moment a visitor identifies (form fill, chat identification), their entire anonymous history is linked to the new contact and drives its first-touch attribution.