ENGBP Peak Team

Marketing Attribution for Small Business: What Customers Say vs What Dashboards Claim

The dashboard said Facebook; the customers said the map pin. Mine "how did you hear about us" from calls and chats — the honest source table in 60 days.

A Bangkok dental clinic spent ฿40,000 a quarter on boosted posts because the dashboard said Facebook "drove" most bookings. Then the front desk started actually asking — and logging — "how did you hear about us?" for two months. The truth: the biggest source was the Google Maps pin near the BTS exit, second was a customer who kept sending her colleagues, and Facebook brought mostly price-shoppers who never booked. The ad budget had been optimizing a lie.

Your analytics dashboard tracks clicks. Your customers, in their own words, tell you what actually made them choose you — and almost nobody writes that down. Explainer: the five-step attribution pipeline — fetch + filter, extract the verbatim quote, classify against fixed buckets, store idempotently — and the honest source table it produces after 60–90 days

This guide shows how to capture it automatically: the "how did you hear about us" pipeline, built from call notes, LINE chats, and intake forms.

Marketing attribution for a small business is best captured from what customers actually say, not from click tracking. Build a simple pipeline: every new customer conversation (calls, LINE, intake forms) gets one AI pass that extracts any "how did you hear about us" moment as a verbatim quote, and a second pass that tags the quote into fixed buckets (Google Maps, walk-by, friend referral, Facebook, TikTok, event). Stored in a sheet keyed by customer, this turns anecdotal guesswork into a countable source table — the honest version of where your customers come from. :::

Why dashboards lie a little

Click attribution registers the last touch. A patient who saw your TikTok three weeks ago, checked your Google reviews, walked past the clinic, and finally clicked a Facebook ad gets counted as "Facebook." The ad gets credit for work the map pin and the reviews did.

What customers say out loud is messier but truer: "My colleague Khun Pim kept recommending you" or "I searched 'dentist near BTS' and you were the closest with good reviews." No pixel captures that. Your transcripts and chat logs already contain it — the only question is whether anyone reads them all. (Usually nobody does, which is why this is an automation job — the same conversation-mining pattern we covered before, pointed at one specific question.)

Click attribution Asked-and-logged attribution
Measures Last touch before booking What the customer credits
Catches word-of-mouth Never Yes, with names
Catches offline (walk-by, events) Never Yes
Cost Ad-platform interpretation Your own pipeline
Best for Ad tuning Budget and strategy decisions

The pipeline: five small steps

Follow the blobs-to-blocks rule — one job per step, structured outputs between.

Step 1 — Fetch

Nightly, pull yesterday's conversations from wherever they live: LINE OA export, call-recording transcripts, booking-form free-text fields. Store raw, mark pending.

Step 2 — Filter

Not every conversation contains an attribution moment. A cheap pre-pass (or a simple keyword scan for "heard about," "recommended," "saw your," "แนะนำ," "เห็น") keeps only the ones worth an AI call.

Step 3 — Extract (AI call #1)

One narrow job — find the moment, quote it exactly:

{
  "task": "find any mention of HOW this customer found the business",
  "output": {
    "found": true,
    "quote": "verbatim customer words, or empty",
    "speaker": "customer",
    "confidence": "high | medium | low"
  },
  "hard_rules": ["quote must be verbatim", "no quote → found: false", "JSON only"]
}

Step 4 — Classify (AI call #2)

A second, separate call tags the quote against a fixed bucket list — never free-form labels, or your sheet fills up with synonyms:

buckets:
  - google_maps      # "searched dentist near BTS", "on the map"
  - google_reviews   # "saw the reviews"
  - walk_by          # "my office is next door"
  - friend_referral  # names, "my colleague recommended"
  - facebook
  - tiktok / instagram
  - event_or_partner
  - returning_customer
  - other

Separating extract from classify is what makes it reliable — one AI call doing both at once is exactly the multi-objective blob that drifts and invents.

Step 5 — Sanitize and store

Validate the JSON, check the bucket is on the list, then store keyed by customer/booking ID:

sheet columns:
  date | customer_ref | bucket | verbatim_quote | confidence | source_doc

Keying by customer ID gives you idempotency — rerun the night's batch twice, you still get one row per customer. Within a month you have the table the front desk could never sustain by hand.

The monthly payoff

After 60–90 days of rows, the source table answers real money questions:

# monthly source rollup (your sheet, one pivot away)
friend_referral   34%  ▓▓▓▓  ← highest booking rate, zero spend
google_maps       27%  ▓▓▓   ← is your pin optimized?
walk_by           14%  ▓▓
tiktok            11%  ▓
facebook           9%  ▓     ← ฿40k/quarter... for this?
event_or_partner   5%  ▓

Decisions fall out of that table: double down on the referral program, spend a weekend on the Google profile instead of another boost, ask TikTok viewers to book directly. The grid scan then shows you exactly where that Maps visibility holds and breaks across the city.

Guardrails that keep it honest

  • One AI job per call — extract and classify as separate blocks
  • Fixed buckets only — "other" exists so the model never invents category names
  • Verbatim or empty — no paraphrase; if there's no quote, there's no row worth trusting
  • Rate limits — a 1-second delay between calls when backfilling a backlog
  • Idempotent keys — customer/booking ID as the primary key, no duplicates on rerun

Beyond attribution: the same pipeline, retargeted

The extraction prompt is the only part that changes:

  • Competitive mentions — extract every competitor name + the customer's sentiment
  • Pain points — "what almost stopped you from booking?"
  • Feature requests — "what do you wish we offered?"

Same fetch-filter-extract-classify-store machinery, new question. Build it once, point it at whatever your next strategy decision needs.

Frequently Asked Questions

Do I need a programmer to build this?

No — n8n or Make provide the blocks (schedule, API, AI call, sheet write), and the AI part is two short prompts with JSON outputs. The thinking work is designing your bucket list and keeping the quotes verbatim, not writing code.

Why two AI steps instead of one?

Extraction and classification are different mental jobs; combined, they produce drift and invented categories. Separate calls with a fixed bucket list make each step checkable and the data consistent — the same reliability rule as every pipeline in this series.

What if a customer mentions multiple sources?

Extract them all — the extraction prompt returns every found moment, and the loop classifies each mention as its own row. Multiple sources per customer is signal, not noise; it tells you which channels assist and which one closes.

How long before the data means anything?

Give it 50–100 logged customers. Below that, read it as a collection of quotes (still useful); above it, the percentages start being trustworthy — and almost always different from what the ad dashboard claimed.

Final note

Your customers already told you why they chose you. The only question is whether that answer lands in a spreadsheet or evaporates at the front desk. One small pipeline, five blocks, and the truest marketing report you've ever had writes itself.

Get new articles by email

Local SEO checklists and tips, sent when new ones drop. Unsubscribe anytime.