# Dynamic Promotion — full content corpus
> Built for search. Ready for AI. Web design + SEO + AEO agency, Denham Springs, Louisiana.
> This file is auto-generated from the site's content collections on every
> deploy. Curated table of contents: https://dynamic-promotion.com/llms.txt
Site: https://dynamic-promotion.com
Contact: office@dynamic-promotion.com
========================================================================
# Guides
========================================================================
## Content strategy for trades: the six disciplines that win
URL: https://dynamic-promotion.com/guides/content-strategy-for-trades/
Summary: How question-format H2s, specific-data claims, real customer quotes, and a 90-day freshness cadence compound into trades-content that wins SEO and AI citations.
Run the thought experiment: two roofers in the same metro publish roughly the same volume of content for a year. One ships 18 guides across the year on topics like "How long does a roof last in Cleveland," "Storm damage roof inspection checklist," and "Insurance claim timeline for hail damage." The other ships 24 blog posts on topics like "Top 5 reasons to call us for your roofing needs" and "Why we're the best in town for asphalt shingles." Everything this pillar covers predicts the same ending: the first roofer becomes the cited answer when a homeowner asks Perplexity or ChatGPT about roof replacement, and the second is invisible in those answers while grinding out positions 6 to 12 on Google. Same metro, roughly equal investment, wildly different outcome.
This pillar is the trades-vertical content discipline. It defines what makes a roofing or plumbing or electrical contractor's content win, walks through the six editorial disciplines that move the needle, gives you a publishable first-year content calendar with monthly cadence, and ends with a 10-point scorecard you can use to rate any page on your existing site. The [schema graphs pillar](/guides/schema-graphs-for-trades/) covers the JSON-LD layer that wraps this content. The two work as a pair.
## What trades content has to do that other content doesn't
Trades businesses sell into a buying decision that's unusually compressed and unusually high-stakes. A homeowner who needs a roof replaced isn't comparison-shopping for six months. Sometimes the decision happens in 48 hours because the storm just hit. The content has to do its work fast, has to anchor trust in the absence of any prior relationship, and has to answer specific operational questions a customer will Google in the middle of the night.
The second factor, often overlooked: most trades customers are one-shot buyers. A roof gets replaced every 20 to 30 years. A water heater every 8 to 12. The repeat-purchase economics that drive SaaS or DTC content (drip campaigns, retention marketing, lifetime-value optimization) don't apply. Every piece of trades content is fighting to win a first impression against competitors a homeowner has no prior context for.
Both factors push the editorial bar higher than B2B or ecommerce equivalents. Specific data outperforms generic claims. Real customer testimonials outperform unsourced "great service" quotes. Hyper-local depth outperforms metro-wide generalization. Genuine expertise (the kind only someone who has actually replaced a roof can produce) outperforms ghostwritten generalist copy. The six disciplines below cover the specific editorial moves that compound.
## The six disciplines that win trades content
Every guide and service page on a trades site should satisfy all six. A page that satisfies four is acceptable. A page that satisfies two has minimal AEO weight and middling SEO weight. The disciplines are independent; missing one is a real gap, not an acceptable trade-off.
1. Question-format H2s anchored to real customer queries
H2 headings written as the questions a customer would type into Google or ChatGPT, not as topic labels. "How much does roof replacement cost in Cleveland?" outperforms "Pricing." The question version anchors AI extraction; the topic version doesn't. Source the questions from your sales team's actual inbox or from your Google Business Profile Q&A section.
2. Specific data with verifiable sources, not generic adjectives
Every numeric claim should match a real source the reader could check. "2-3 day average from contract to install completion" outperforms "fast turnaround." "GAF Timberline HDZ shingles with 50-year manufacturer warranty" outperforms "premium materials." Numbers without sources are still better than adjectives without sources, but the strongest pattern pairs the number with the attestation.
3. Real customer quotations marked up with Quotation schema
Every service page should embed at least three verbatim quotes from real customers, attributed by first name + last initial + city, linked back to the verifiable source review URL. Quotation schema (the Schema.org type, walked through in the schema graphs pillar) gets AI engines to extract these as citable passages. Fabricated testimonials are detected at scale and create legal exposure. Real ones compound.
4. Hyper-local depth, one page per (service, city)
A roofer serving five cities builds five pages per service, not one. Each city-specific page carries content the others don't: neighborhoods, building codes, storm history, local roof types, real customer reviews from that specific city. Generic "we serve Cleveland, Akron, Canton, and surrounding areas" pages rank for none of those cities. Per-city depth wins in both Google's Local Pack and AI Overviews.
5. Freshness discipline — substantive updates on a 90-day cycle
Pages updated in the last 90 days get cited by AI engines at roughly double the rate of stale pages. Trades-relevant numbers change predictably: pricing rises, materials evolve, codes update, storms shift the local market. Build a rolling update cadence. Three pages a month, every month, every page revisited at least twice a year. The dateModified field has to match a real substantive change, not a one-word tweak to satisfy a calendar reminder.
6. Inline citations to authoritative external sources
Linking to peer-reviewed studies, government regulations, industry-association guidance, and manufacturer documentation increases your citation likelihood. AI engines treat outbound links to authoritative sources as a credibility signal. The intuition that linking out hurts your SEO is empirically wrong for AEO; the data goes the other direction.
The six are independent disciplines. Two pages on the same site can satisfy different subsets. The goal isn't to monolith every page into all six at once. The goal is to make sure no page on the site is missing more than two.
## How to find the question-format H2s your customers actually search
The mistake most trades content makes on this dimension is writing H2 questions a homeowner would never actually ask. "What considerations matter when selecting a roofing contractor?" is not the question. "How do I tell if my roofer is licensed?" is. The phrasing matters because AI engines anchor extraction to headings that match the user's prompt.
Three sources produce the real questions:
1. **Your sales team's inbox.** Search the last 90 days of customer-introduction emails for question marks. The questions homeowners ask during the sales conversation are usually the questions they Googled first.
2. **Your Google Business Profile Q&A section.** The questions the public posts there are real customer questions, formatted as customers actually phrase them. Don't paraphrase. Use the language verbatim.
3. **AI engines themselves.** Open ChatGPT or Perplexity. Ask "what questions do homeowners ask about roof replacement in Louisiana?" The AI returns a curated list of real-world questions. Use it as a draft outline. Validate against #1 and #2.
The reason this is hard is that the questions homeowners actually ask are often embarrassingly direct ("how much does it cost?" "how long will my house be a mess?" "can I stay home during the work?") and trades-business owners often want to write content that sounds more sophisticated. Resist that pull. The direct phrasing wins.
## Specific-data H2s and what numbers to publish
The pattern that produces the biggest AEO lift in this category: replace any sentence in your content that contains a generic claim with a sentence that contains a specific, sourced, defensible number.
A few examples from a typical roofing service page, before and after:
| Generic (low AEO weight) | Specific (high AEO weight) |
|---|---|
| "Fast turnaround" | "2-3 day average from contract signature to install completion across the last 247 jobs" |
| "Affordable pricing" | "$7,500-$18,000 typical range, depending on roof size, pitch, and tear-off requirements" |
| "Experienced team" | "12 crew members, 8 year average tenure, all OSHA-30 certified" |
| "Quality materials" | "GAF Timberline HDZ shingles with 50-year manufacturer warranty (top 3% of installer sales volume = GAF Master Elite eligibility)" |
| "We serve all of Cleveland" | "Serving Cleveland, Akron, Canton, and the I-77 corridor; 247 roofs installed in Greater Cleveland in 2024" |
The right-column versions run longer, and the length is doing work: AI extractors pull substantive sentences and ignore generic ones, so the extra words are exactly the part that earns the citation.
The discipline to enforce: every number on the page has to be defensible. If you say "247 roofs in 2024," that number should match what your job-management software shows on demand. If you say "8 year average tenure," the team roster has to prove it. Specific data that isn't verifiable is worse than no data at all, because it's a claim a regulator, competitor, or attorney could pick apart.
“
Helpful content is content that's helpful to people, not content created primarily to attract visits from search engines.
—Google Search Central, Helpful content update announcement, 2022
That guidance is the core of every Google content-quality update since 2022. The specific-data discipline is one of the most direct ways to satisfy it on a trades site. Generic content is always written for search engines first. Specific, defensible content is always written for a real reader who could verify the claims independently.
## Real customer quotation discipline
Every service-detail page should embed at least three verbatim quotations from real customers, sourced from your Google Reviews, attributed by first name plus last initial plus city, and linked back to the verifiable source URL. The reason this works for trades specifically is that homeowners hiring contractors are in a low-trust, high-stakes context, and a quote from a real previous customer is one of the few things that immediately moves trust.
The minimum viable pattern:
```html
"They replaced our roof in two days flat, came in $400 under the estimate, and the crew cleaned up better than we did the day we moved in."
— Sarah M., Watson, LA · Google Review · 5★ · April 2026
```
For maximum AEO weight, add Schema.org `Quotation` markup as inline JSON-LD or microdata. For maximum Google rich-results entitlement, add `Review` schema too. The two coexist; they describe the same underlying quote from different angles. The [schema graphs pillar](/guides/schema-graphs-for-trades/) walks through the schema choice in detail.
The rules that protect the system are non-negotiable:
- **Never fabricate quotes.** Detected at scale through cross-referencing against the actual Google Review record. Creates legal exposure even when the customer is fictional.
- **Never edit a customer's words substantively.** Ellipsis (`...`) and clarifying brackets (`[their roofer]`) are fine. Rewriting the substance is not.
- **Always link to the verifiable source.** A Google Review attribution that doesn't link to the source review URL gets weighted lower by AI engines than one that does.
The cadence that produces sustainable review supply: send a personalized text or email to every completed-job customer within 48 hours with a direct link to your Google Review form. No incentives, ever. Google's policy bans them, and the algorithm increasingly catches review-for-payment patterns.
## Hyper-local content depth: a page per (service, city)
The structural mistake most trades sites make is one page per service, mentioning every city served as a list. That page ranks for none of the cities, gets cited by no AI engine in city-specific queries, and looks like every other generalist contractor page in the metro. The fix is straightforward to describe and tedious to execute: build one page per (service, city) combination for every commercially significant pairing.
A roofer serving Greater Baton Rouge would build something like:
- `/services/roof-replacement/baton-rouge/` for Baton Rouge specifically
- `/services/roof-replacement/watson/` for Watson
- `/services/roof-replacement/denham-springs/` for Denham Springs
- `/services/roof-replacement/zachary/` for Zachary
- `/services/roof-replacement/central/` for Central
Each city page carries real, substantive content the others don't:
- Specific neighborhoods and developments where you've worked
- Local building codes and HOA rules that affect roofing decisions
- Common roof types in that area (Watson trends older shingle homes; Baton Rouge has more historic slate)
- Storm history specific to that city (Hurricane Ida's effect on Baton Rouge differs from Watson's)
- Real customer review quotes from customers in that specific city
- Service-radius notes and driving directions
- Pricing ranges specific to that local market when you can defend them
This is also where the AEO and local SEO disciplines compound. A city-specific page with question-format H2s, real Google review quotes, and a populated LocalBusiness schema for the service area becomes the page AI engines cite when a homeowner asks "best roofer in Watson." The [local SEO essentials pillar](/guides/local-seo-essentials/) covers the foundation that makes this pattern work.
## The freshness discipline
AI engines weight recency aggressively for trades-relevant queries. The assumption is that pricing, materials, codes, and offerings change often enough that stale content is unreliable. Pages updated in the last 90 days get cited at roughly double the rate of pages 12 months stale.
The four signals that have to align:
90dfreshness window for top AEO citation weightDP working heuristic from platform-tracking write-ups
2×citation rate lift vs. pages 12+ months staleDP working heuristic; no GEO-paper-grade study yet
3/mominimum substantive update cadence for tradesDP recommended baseline
2×/yrminimum revisit per existing high-value pageDP recommended baseline
The substantive part matters more than the cadence number. A one-word change to satisfy a calendar reminder doesn't trigger the freshness signal. Google's algorithm looks at the actual content delta. Real freshness updates include:
- Pricing range adjustments backed by your actual job records
- New customer review quotes added from the last 30-60 days
- Updated photos from recent jobs in the relevant city
- Material or product changes (new shingle warranty, updated certification status)
- Code or regulation updates relevant to the topic
- New first-party data points from your own operational records
The `dateModified` field on your page's Article or LocalBusiness schema has to match the most recent substantive change. Faking it is detectable and erodes the very freshness signal you're trying to manufacture.
## The first-year content calendar
For a trades business starting from a baseline of weak existing content, the first 12 months follow a defensible rhythm. The shape below assumes the business has 4-5 core services and operates across 3-5 cities in a metro. Adjust upward for larger operations, downward for solo operators.
| Month | Publish (new) | Revise (existing) | Focus |
|---|---|---|---|
| 1 | Foundation home page rewrite | Top 3 service pages | Establish baseline disciplines on highest-traffic pages |
| 2 | 2 service-detail pages (top 2 services) | 1 existing service page | Add specific data, quotes, schema |
| 3 | 2 hyper-local pages (top 2 cities, top 1 service) | 1 service page | Begin per-city depth pattern |
| 4 | 2 guides (first guide pillar + 1 spoke) | 1 hyper-local page | Establish editorial cadence |
| 5 | 2 hyper-local pages (next 2 cities × top service) | 2 service pages | Continue local depth build |
| 6 | 2 guides (second pillar setup) | 1 hyper-local page | Mid-year content audit and review-cadence check |
| 7 | 2 guides (spokes off pillar 2) | 2 service pages | Refresh data, integrate new reviews |
| 8 | 2 hyper-local pages (services 3-4 × top cities) | 2 hyper-local pages | Continue grid coverage |
| 9 | 2 guides (industry-trend pieces, seasonal) | 1 pillar | Q3 freshness pass |
| 10 | 2 hyper-local pages (round out grid) | 2 service pages | Hit majority of (service × city) cells |
| 11 | 2 guides (audit-driven spokes filling gaps) | 1 pillar | Address weakest pages from analytics |
| 12 | 2 high-value updates instead of new pieces | 4 service pages, full refresh | Annual freshness audit and Q4 update push |
That's roughly 24 new pages and 23 substantive revisions over 12 months. The total publishing rhythm is 1 new + 1 revision per week, averaged. Some weeks will be heavier, others lighter. The constraint is that no week should pass without either a new piece or a substantive revision touching the site.
The calendar assumes the business has the editorial capacity to produce this volume in-house or through a regular contributor. If the realistic capacity is half that, halve every row and accept the slower compounding. A roofer publishing 12 substantive pieces a year still outpaces 80% of competitor sites in mid-tier US metros, where the typical pattern is 0 to 4 pieces a year.
## Common objections — and the honest answers
The pushback this content discipline gets, in the field:
**"Our customers don't read this kind of content."** They don't read all of it. They read the parts that matter to their specific question. A 2,500-word guide that's structurally extractable lets them land on the H2 that answers their question and read 200 words. The other 2,300 words exist for the next ten customers with different specific questions. Length isn't a usability problem when the structure is right.
**"We don't have time to publish 24 pieces a year."** Most of the time, that's true at first. The output ramps. Month 1 produces less than month 6. Building the editorial template (question H2 inventory, quote-collection rhythm, photo library, service-list reference) front-loads the time investment. By month 4 the per-page time drops by 40-60% because the components are reusable. The capacity constraint is real, but it eases on a predictable curve.
**"Won't this stuff get scraped and used against us by AI engines?"** AI engines extract and credit. Refusing to publish doesn't prevent the extraction; it just ensures the extracted answer comes from a competitor who did the work. The asymmetric outcome favors publishing. If your content is good and your schema is clean, you become the cited source. If you don't publish, someone else gets cited as the authority in your market.
**"Can't we just use AI to write all of this?"** No. AI-generated content without human expertise and verifiable data underperforms across every metric that matters here. AI engines preferentially cite content with E-E-A-T signals (real authors, verifiable credentials, defensible data, real customer quotes). Pure AI-generated content has none of those. Use AI as a drafting assistant for the structural template, then layer in the trade-specific expertise from your team. That works. Letting AI write the whole thing doesn't.
**"How do we measure whether this is actually working?"** Three metrics over a 90-day rolling window. GA4 referral traffic from chatgpt.com, perplexity.ai, claude.ai, and gemini.google.com. Manual prompt audits of your top 10 commercially-important queries across those engines. Google Search Console impression growth on long-tail informational queries. Move all three in the right direction and the discipline is paying off. If only one moves, your content is winning on one surface but missing on others; audit which discipline is weak.
## The trades-content scorecard
Take any existing page on your site. Rate it on each of the 10 criteria below, 0 or 1. A page scoring 8+ is competitive in your metro. A page scoring 5-7 has real gaps but can be improved page-by-page. A page scoring under 5 is unlikely to compound and should be rebuilt rather than edited.
1. **Question-format H2s** — Headings phrased as the questions a real customer would ask
2. **First-sentence answers** — The first 1-2 sentences after each H2 directly answer that H2's question
3. **Specific data with sources** — Generic adjectives ("fast," "affordable," "premium") replaced with sourced numbers
4. **Real customer quotations** — At least three verbatim quotes from real reviews, attributed and linked
5. **Schema markup** — `LocalBusiness` subtype, `Service`, `FAQPage`, and `Quotation`/`Review` schema present and validated
6. **Inline citations** — At least three outbound links to authoritative external sources within the body
7. **Hyper-local depth** — Content specific to a named city (neighborhoods, codes, storm history, local context), not metro-generic
8. **Freshness** — `dateModified` reflects a substantive update within the last 90 days
9. **HTML-first** — Critical content visible in View Source, not rendered by client-side JavaScript
10. **E-E-A-T signals** — Named author byline with credentials, team-member `Person` schema with `worksFor` and `hasCredential`
Run the scorecard on your 10 highest-traffic pages before rebuilding anything new. Most trades sites discover that 3 to 5 of those top pages score under 5, and those are the highest-leverage improvements. The [AEO fundamentals pillar](/guides/aeo-fundamentals/) covers the underlying AEO mechanics. The [schema graphs pillar](/guides/schema-graphs-for-trades/) covers the schema layer. The [local SEO essentials pillar](/guides/local-seo-essentials/) covers the local SEO foundation. The four pillars together describe the full discipline.
---
## Local SEO essentials: how service businesses win in 2026
URL: https://dynamic-promotion.com/guides/local-seo-essentials/
Summary: How Google Business Profile, real reviews, NAP consistency, and LocalBusiness schema combine to win the Map Pack for service businesses in 2026.
For most service businesses, local search is the whole pipeline. A roofer in Watson. A plumber in Denham Springs. A dentist in Baton Rouge. Almost every new customer started with a phone in their hand and a Google query that mentioned a place. The Map Pack and the AI-powered local recommendation panel sit above every other source of traffic for those businesses combined. **If you're invisible there, the rest of your marketing is downstream of a closed faucet.**
This pillar is the foundation of everything Dynamic Promotion will publish about local SEO. It walks through the three signals Google uses to rank local results, the role Google Business Profile plays at the center of every local search, the structural data work (NAP consistency, LocalBusiness schema, citations) that earns algorithmic trust, and how reviews now do double duty as a ranking signal and an AI citation signal. Spokes from this pillar go deeper into individual tactics. This pillar is the one you read first.
## What is local SEO, and why does it matter for service businesses?
Local SEO is the practice of getting your business to rank when someone searches for a service in a specific place. The "place" part is what separates it from regular organic SEO. Google detects geographic intent in two ways. First, an explicit place name in the query ("plumber in Watson"). Second, an implicit one, where Google uses the searcher's location to assume they want a nearby result ("plumber" typed into a phone in Watson returns Watson plumbers regardless of whether the query said so).
When Google detects local intent, the search results page changes shape. Above the regular organic ten blue links sits the **Map Pack** (also called the Local Pack or 3-Pack), a boxed listing of three local businesses with a map, ratings, hours, and direct call buttons. The Map Pack absorbs the majority of clicks on local queries. Ranking organically below the Map Pack still matters, but the click distribution is heavily front-loaded. Position 1 in the Map Pack outperforms position 1 in the regular organic list by a factor of several times for the same query.
The Map Pack runs on different signals than the regular organic algorithm. Your Google Business Profile is the anchor. Your reviews, your photos, your service area, your category, your NAP consistency across the web, and the proximity of the searcher to your verified address all factor in. Most of these signals don't matter for traditional organic ranking at all. That's why a national chain can outrank a local contractor on a generic search and still lose the Map Pack to the same local contractor on the location-qualified version of that search.
The business case is straightforward. Google's most-cited figure on local search, [published via Think with Google](https://www.thinkwithgoogle.com/marketing-strategies/search/local-search-mobile-marketing-statistics/), put local-intent queries at roughly 46% of all searches; the figure is a decade old now, and every signal since points the share higher, not lower. For a service-vertical business, that share is higher. Most contractors, dentists, lawyers, and trade professionals draw 70% or more of their search traffic from queries with explicit or implicit local intent. The local SEO program isn't a tactical add-on. It's the lead-generation engine.
## The three signals Google uses to rank local results
Google has published the official ranking logic for local results, and it's simpler than most algorithms in this domain. The [Google Business Profile help center documents](https://support.google.com/business/answer/7091) three signals that drive local rankings:
“
Local results are based primarily on relevance, distance, and prominence. A combination of these factors helps us find the best match for your search.
—Google, Business Profile Help Center, "How Google determines local results"
Relevance, distance, and prominence are the three. Each one rewards a different kind of work.
Relevancehow well your profile and website match the searcher's queryDriven by category, services, GBP description, and on-site content
Distancehow close your verified address is to the searcher's locationA signal Google measures, not one you can fake. Verify a real address.
Prominencehow well-established and trusted your business is onlineDriven by reviews, citations, links, brand mentions, and GBP activity
**Relevance** is the most controllable of the three. Your primary GBP category, your service list, and your website content all contribute to whether Google reads your business as the right match for a query. A roofer whose primary category is "Roofing contractor" with a populated services list (roof replacement, storm damage repair, gutter installation) signals more relevance for the query "roof repair in Watson" than a competitor whose primary category is the broader "Contractor."
**Distance** is the least controllable. The searcher's location at query time anchors the calculation. The closer your verified address is to that location, the higher you tend to rank. There's no shortcut here. Multi-location businesses get to compete in multiple proximity zones, but each location needs to be a real, verifiable place where the business operates.
**Prominence** is the longest-running investment. It's a synthesis of every signal that suggests your business is established, trusted, and active. Review count and freshness, citation consistency across the web, inbound links, brand mentions in news and editorial content, and the recency of your GBP activity all roll into prominence. This is the signal that compounds over years. A business with two years of consistent prominence work outranks one with six months of work, even when relevance and distance favor the newer business.
## Google Business Profile is the single biggest local lever
Of every asset a local business can invest in, the Google Business Profile produces the highest return per hour of work. [Whitespark's Local Search Ranking Factors survey](https://whitespark.ca/local-search-ranking-factors/), conducted with input from dozens of working local SEO practitioners, has put GBP signals at the top of the Map Pack ranking weight for several survey cycles in a row.
The components of a complete GBP listing:
- **Verified address** that matches the address used everywhere else (website, schema, directories)
- **Primary category** that reflects your core service, not the broadest possible category
- **Secondary categories** for the adjacent services you also offer
- **Service list** with descriptions, prices when relevant, and matching service-area coverage
- **Hours of operation** kept current, including holiday hours configured in advance
- **Photos** refreshed at least quarterly. Exterior, team, in-progress work, and finished jobs.
- **Description** populated with your core value proposition in 750 characters or less
- **Q&A section** actively monitored, with the most common questions seeded by you with your own answers
- **Posts** published weekly to keep the listing active
- **Reviews** collected steadily and responded to within 48 hours
That list reads like busywork. It is busywork. The reason it works is that GBP scores active maintenance as a trust signal. A profile that gets touched weekly outranks an equally-rated but neglected profile in the same proximity zone. The compounding effect over six months is substantial.
The most overlooked component is the Q&A section. Most owners never look at it. Google opens the Q&A as a public forum, and any user can post a question or an answer to your listing without your involvement. Bad actors and confused customers populate it with misinformation when you're not watching. The corrective work is straightforward: seed the section yourself with the 5 to 8 questions your real customers ask most often, answer them in your own voice, and check the section weekly for new public questions.
## NAP consistency: the unsexy work that pays
NAP stands for Name, Address, Phone. Those are the three identity fields every directory, schema block, and citation references about your business. NAP consistency means writing them the same way every place your business appears on the web.
Inconsistency is the silent killer of local SEO. A business listed as "Watson Roofing Co." on Google, "Watson Roofing Company" on Yelp, "Watson Roofing" on Facebook, and "Watson Roofing Co" (no period) on BBB looks like four slightly different businesses to Google's deduplication algorithms. The aggregate signal each listing should contribute gets fragmented across four entity records, none of which receives full algorithmic credit.
The discipline is mechanical. Write your business name, address, and phone number once, exactly as you want them to appear, in a NAP reference document. Then audit every existing listing against that reference and fix anything that doesn't match. Going forward, every new directory submission copies from the reference.
Do this
Pick one canonical NAP. Write it down. Include every detail: business name spelling, suite numbers, abbreviations vs. spelled-out words ("St" vs "Street"), phone number format with consistent dashes or dots.
Then run a free NAP scan at Moz Local or BrightLocal. Both surface every existing listing for your business and flag inconsistencies. Fix the top 10 directories first. The long tail can wait.
The reason NAP consistency matters more in 2026 than it did in 2018 is that AI engines now use the cross-reference graph to verify entities. When an AI summarizer is deciding whether to recommend you, it pulls your `LocalBusiness` schema, then checks whether the same address shows up on Yelp, BBB, Facebook, and your state's secretary of state filings. If those records agree, the AI's confidence in citing you goes up. If they conflict, you get flagged as a low-confidence entity and skipped in favor of a competitor whose records line up.
NAP consistency is also where most agencies short-change their clients. The work is tedious, takes weeks to complete on a neglected business, and produces no immediately visible UI change on the website. It compounds invisibly. Owners who skip this step rarely connect the lack of visible ROI to the underlying inconsistency.
## LocalBusiness schema and structured data essentials
[Schema.org's `LocalBusiness`](https://schema.org/LocalBusiness) type is the structured-data root that every service business needs on their website. It tells search engines (and increasingly, AI engines) the canonical machine-readable version of your business identity. The basic shape, as JSON-LD inside a `
```
A few rules that separate strong schema from weak schema:
1. **Use the most specific subtype.** Don't write `"@type": "LocalBusiness"` when there's a more specific subtype like `RoofingContractor`, `Plumber`, `Dentist`, `HVACBusiness`, `Electrician`, or `Attorney`. [Google's local-business structured data guide](https://developers.google.com/search/docs/appearance/structured-data/local-business) lists the canonical subtypes. The specific subtype gives AI engines a tighter category signal.
2. **Populate `sameAs` with at least 4 verified URLs.** This array is how AI engines verify your entity exists. Include your verified GBP listing URL, your Facebook page, your BBB listing, your LinkedIn company page, and any industry-directory listings (Angi, HomeAdvisor for home services, Healthgrades for medical, etc.).
3. **Match the schema NAP to the GBP NAP exactly.** Every field (name, street address, suite number, phone format, postal code) has to be byte-identical to what's in your Google Business Profile. Any mismatch creates entity-disambiguation problems for AI engines.
4. **Include `geo` coordinates.** The latitude and longitude help AI engines validate the address corresponds to a real place at a real location, not a virtual office shell.
5. **Validate with [Google's Rich Results Test](https://search.google.com/test/rich-results) before deploying.** Schema errors silently downgrade your eligibility. Test every page that emits `LocalBusiness` schema, not just the home page.
For multi-location businesses, the rule is one canonical `LocalBusiness` node per real location, each with its own `@id`, each linked to a per-location page on your site (`/locations/watson/`, `/locations/baton-rouge/`, etc.). The `LocalBusiness` node on your home page should represent your primary or headquarters location.
## Reviews are now a ranking AND citation signal
Reviews have always been important to local SEO. The 2026 update is that they're also one of the most powerful AEO signals. AI engines pull verbatim review quotations into recommendation answers far more readily than other content types, because review text is short, evaluative, attributable to a real person, and easily verifiable through the source platform.
The [BrightLocal Local Consumer Review Survey](https://www.brightlocal.com/research/local-consumer-review-survey/) has tracked consumer behavior around online reviews for over a decade. The headline findings from the 2024 survey:
98% of consumers read online reviews for local businesses, and 76% trust online reviews as much as personal recommendations from friends and family.
Findings summarized from
BrightLocal, Local Consumer Review Survey (2024)
The signals Google reads from reviews are broader than star count:
- **Review velocity.** Are new reviews showing up steadily, or did 80 of them arrive in a 2-week burst three years ago? Steady drip outperforms occasional flood.
- **Review recency.** A business whose most recent review is six months old looks less active than one with a review from last week.
- **Review response rate.** Owners who respond to reviews (positive and negative) signal active engagement. Google reads response rate as a freshness signal in its own right.
- **Review content depth.** "Great service!" carries less weight than "They replaced our roof in 2 days, came in $300 under their estimate, and the crew cleaned up better than we did." Length and specificity matter.
- **Geographic distribution of reviewers.** Reviews from people within your actual service area carry more weight than reviews from out-of-state visitors.
- **Photo attachments.** Reviews with photos signal an actual customer interaction, not a hired review farm.
The AEO layer on top of all that: every verbatim review quote you embed on your own service pages (with proper `Quotation` schema, attribution, and a link back to the source review URL) compounds your citation likelihood. A roofing service page with three real Google review quotes marked up as Quotation is materially more AEO-friendly than the same page with no quotes.
Do this — embed real reviews
Real, attributed, linked
"Watson Roofing replaced our roof in two days. The crew showed up at 7am sharp, cleaned up everything before leaving, and we came in $400 under the original estimate."
— Sarah M., Watson · Google Review · 5★ · April 2026
Real customer. Attribution by first name + last initial + city. Linked to the verifiable Google Review URL. AI engines treat this as a high-confidence extractable passage.
Avoid — generic testimonial
Unverifiable, anonymous, paraphrased
"Watson Roofing is the best in town! Highly recommend them to anyone in need of a quality roofer."
— Happy Customer
No attributable person. No verifiable source. Reads as written by the business owner. AI engines route around it, and savvy consumers do the same.
The review-collection rhythm that produces sustainable results is boring. After every completed job, a follow-up text or email goes out within 48 hours with a direct link to your GBP review form. Personalize it. Something like "Hi [name], thanks for choosing us for the [job]. If you have 2 minutes, a Google review really helps a small business like ours." Don't offer incentives. Google's policy prohibits review-for-payment, and the algorithm increasingly catches it.
## Citations and directory listings in 2026
A "citation" in local SEO terms is any mention of your business name, address, and phone number on a third-party website, regardless of whether the citation includes a backlink. Citations were the foundation of local SEO in 2010, and despite a decade of evolution, [Moz's Local Search Ranking Factors study](https://moz.com/learn/seo/local-ranking-factors) still ranks citation consistency and authority among the top off-site signals.
What changed in 2026 is that citation volume matters less than it used to, and citation quality and consistency matter more. The 10 directories that actually move the needle for almost any local business:
1. **Google Business Profile** is mandatory. It's the anchor.
2. **Bing Places for Business**, the second-largest search engine, often overlooked.
3. **Apple Business Connect** feeds Apple Maps, Siri, and Apple Intelligence.
4. **Yelp** still drives meaningful local traffic and feeds many AI systems.
5. **Facebook Business Page** for entity-graph reinforcement plus the social-search surface.
6. **BBB (Better Business Bureau)** as a trust signal, especially for service trades.
7. **Industry directories** like Angi/HomeAdvisor for home services, Healthgrades for medical, Avvo for legal. Pick the 2 to 3 most relevant.
8. **State chamber of commerce or local business association**.
9. **LinkedIn Company Page**, increasingly important for AI-engine entity verification.
10. **Your trade or franchise's official "find a contractor" directory** if one exists.
After these 10, the marginal return on each additional listing drops dramatically. Citation building services that promise 100+ directory listings for $300 are usually building low-quality listings on directories AI engines have already learned to ignore, while leaving NAP inconsistencies in their wake.
The audit-and-fix order is the same for any business:
1. Pull a NAP audit report from Moz Local, BrightLocal, or Whitespark
2. Fix inconsistencies on the top 10 first
3. Submit to any of the top 10 you're missing
4. Set up a quarterly recheck so new inconsistencies (from acquisitions, address changes, or rogue directory scrapes) get caught
## Local content strategy: a page per service area, with real depth
The single most common content mistake on local-business sites: one page per service, mentioning every city served, with no per-city depth. The result is a thin homepage that ranks for nothing and a "service areas" page that's just a list of cities. AI engines and Google's local algorithm both want city-specific depth.
The defensible structure is one page per (service, city) combination that gets real volume. A roofer serving the Baton Rouge metro would build something like:
- `/services/roof-replacement/` for the main service page, broad coverage
- `/services/roof-replacement/baton-rouge/` for Baton Rouge-specific content
- `/services/roof-replacement/watson/` for Watson-specific content
- `/services/roof-replacement/denham-springs/` for Denham Springs-specific content
- `/services/roof-replacement/zachary/` for Zachary-specific content
Each city page needs real, substantive content. Not the same paragraph with the city name swapped. Real local content includes:
- Specific neighborhoods or developments you've worked in (with examples)
- Local building codes, HOA rules, or permit specifics for that municipality
- Common roof types in that area (Watson has more older shingle homes; Baton Rouge has more historic slate)
- Storm history and what it means for the local roofing market (Hurricane Ida's effect on Baton Rouge roofs is a real local story)
- Real Google review quotes from customers in that specific city
- Driving directions or service-radius notes
- Pricing ranges specific to that market when you can defend them
This is also where AEO and local SEO compound. A city-specific page with question-format H2s, real review quotes embedded as Quotation schema, and a populated LocalBusiness schema for the specific service-area becomes the page AI engines cite when a user asks "best roofer in Watson."
The opposite pattern is everywhere. One page that says "we serve Watson, Baton Rouge, Denham Springs, Zachary, Central, Greenwell Springs, and surrounding areas." That page ranks for none of those cities and gets cited by no AI engine.
## Mobile, page speed, and the technical floor
The technical floor for local SEO in 2026 is higher than most business owners realize. Service-vertical customers search on phones, and Google's local algorithm reads page-speed and mobile-experience signals aggressively for local queries.
[Google's Core Web Vitals](https://web.dev/articles/vitals) are the three primary technical signals:
- **LCP (Largest Contentful Paint)** under 2.5 seconds. How fast the main content of your page becomes visible.
- **INP (Interaction to Next Paint)** under 200 milliseconds. How responsive the page is to user interaction (replaced FID in 2024).
- **CLS (Cumulative Layout Shift)** under 0.1. How much the page jumps around as it loads.
For a local-business site, these are not optional. A site that fails Core Web Vitals on mobile gets a measurable demotion in local rankings, and the user-behavior signals (bounce rate, time on site) compound the demotion further. The fastest path to a passing Web Vitals score is the same architecture that wins AEO: static HTML, no client-side framework rendering critical content, optimized images, fonts loaded with `font-display: swap`, and minimal JavaScript on the critical path.
DP standardizes on Astro for every build specifically because it produces static HTML by default. Every page ships with the full content visible on first paint, hits LCP under 1.5 seconds on mid-tier mobile hardware, and passes Core Web Vitals out of the box. The technical work is mostly done by the architecture choice.
Avoid
Page builders that emit dozens of plugin-injected scripts (Elementor on WordPress, most Wix sites with apps installed) routinely fail Core Web Vitals on mid-range Android phones. The site loads acceptably on the owner's iPhone Pro and fails on the customer's 3-year-old budget Android, which is the device most local searches come from.
If you can't change the platform, start with image optimization (WebP, lazy loading below the fold, explicit dimensions on every image) and font subsetting. Those two changes alone usually move a failing site to passing.
## How to measure local SEO success
Local SEO has more honest measurement surface than most marketing channels because the conversion paths are short and observable. The 2026 measurement stack for any service business:
**GBP Insights.** Native reporting inside Google Business Profile. Track monthly: searches that surfaced your profile, profile views, direction requests, calls from the listing, and website clicks. The interesting derivative is the search-term report, which shows the exact queries that triggered your listing in the last 30 days. That's free competitive intelligence about how customers describe the work you do.
**Google Search Console.** Filter to "Discover and Search" performance, then look at queries with geographic terms. A "Country: United States, Region: Louisiana" filter on a Watson roofer's GSC narrows to the queries that matter. Track impression growth and click-through rate monthly.
**Rank tracking for the queries that matter.** Whitespark, BrightLocal, and Local Falcon all offer grid-based rank tracking that shows where you rank in the Map Pack from different physical points within your service area. The grid view is more useful than a single rank because it reveals the proximity gradient that drives local rankings. You might rank #1 from a query origin near your office and #6 from one across town.
**Call tracking.** Most local-business leads come by phone. Without call tracking, you can't attribute calls to specific channels. CallRail or Callsource (or a built-in option from your CRM) lets you route different phone numbers to different channels and measure call volume per channel.
**Review monitoring.** A weekly check of new reviews across GBP, Facebook, Yelp, and BBB. Respond within 48 hours. Track response rate as a KPI. Your own response cadence is the one signal you have direct control over.
**AI citation monitoring** (the new layer in 2026). Run monthly prompt audits of 5 to 10 commercially important queries across ChatGPT, Perplexity, Claude, Google AI Overviews, and Gemini. Track whether you appear, whether you're mentioned by name, and whether you're cited with a link. Free to run manually; dedicated monitoring platforms make sense only after the query list outgrows a spreadsheet.
The cadence that works for most owners is a 30-minute weekly check (reviews, GBP posts, any new questions in Q&A) plus a 90-minute monthly deep-dive (GBP Insights, GSC, rank tracking, AI citation audit). Less than that and you stop noticing trends. More than that and the time isn't paying off.
## Where to start, and in what order
Everything above reduces to a sequence: the three signals Google uses, the Google Business Profile work that anchors every local SEO program, the NAP consistency discipline, the LocalBusiness schema that builds an entity Google and AI engines can verify, the role of real reviews as both a ranking and AEO signal, the directory layer that still matters, the per-city content depth that wins service-area queries, the technical floor on mobile, and how to measure whether any of it is working.
The right order for a business starting from zero: GBP setup and verification first, NAP audit and reconciliation across the top 10 directories second, LocalBusiness schema with full `sameAs` array third, a per-service-area content build fourth, review collection rhythm and response cadence fifth, technical and Core Web Vitals work sixth, ongoing measurement and monthly adjustment seventh.
Expect 60 to 90 days before Map Pack rankings start moving on commercially important queries. Expect 4 to 6 months before citation work fully compounds. Expect AEO citation share to start showing within the first 30 to 60 days of consistent on-site work because AI engines crawl and reweight content more frequently than Google reindexes for traditional ranking.
Every site Dynamic Promotion builds for a local-business client ships with the local SEO baseline described here. GBP setup as part of onboarding. LocalBusiness schema with the full `sameAs` array. NAP audit before launch. Per-city service-area pages. Quotation schema on real review excerpts. Deeper guides on multi-location architecture, review-collection automation, service-area mapping, and Apple Business Connect will hang off this pillar as they publish.
The opportunity in 2026 is the same one it's been for several years running: most local-business sites in mid-tier US markets still ship without `LocalBusiness` schema, with inconsistent NAP across directories, with stale GBP listings, and with no per-city content depth. The early movers who do the work compound disproportionate ranking and citation share. The window is still open. It won't be in three years.
---
## Schema graphs for trades: the one-graph-per-page pattern
URL: https://dynamic-promotion.com/guides/schema-graphs-for-trades/
Summary: How to wire LocalBusiness, Service, Review, and Person schema into one @id-connected JSON-LD graph that wins Google rich results and AI citations.
Picture two roofing contractors shipping identical-looking service pages in the same zip code. Same hero photo style. Same "Get a free quote" CTA. Same five trust badges. Both get the same Google Lighthouse score. In that scenario, one keeps showing up in Perplexity recommendations and Google's AI Overviews for "roof replacement" queries while the other doesn't, and nobody on the losing team can figure out why. The difference is almost always in the JSON-LD. The first site emits one connected schema graph per page with `@id`-linked entities. The second emits five loose schema blocks that look right in isolation and tell the AI engine nothing about how the entities relate.
This pillar is the JSON-LD reference for trades businesses. It covers the structural pattern (one graph per page, not loose blocks), the subtype selection that pins your business to the right Schema.org branch, the node-by-node walkthrough of what a complete trades-business graph contains, and the validation toolchain that catches the silent errors. Code samples are real and copy-pasteable. The trade names in the examples are interchangeable. Pick the one closest to your business and substitute.
## One graph, not loose blocks — the structural mistake most trades sites make
The default WordPress + Yoast pattern, and the default for most page-builder sites, is multiple `
```
Three nodes, three `@id`s, two cross-references. The `WebSite.publisher` points to `#org` instead of repeating the `RoofingContractor` data. The `WebPage.isPartOf` points to `#website` instead of repeating the `WebSite` data. The crawler now reads this as one connected entity graph instead of three disconnected ones, and every page across the site that uses the same `#org` and `#website` `@id`s contributes to the same coherent graph.
That last point matters more than the rest of this section combined. Sitewide entities like `Organization` and `WebSite` keep the same `@id` across every page on the site. Page-specific entities like `WebPage` and `Article` include the page path in the `@id` so they're unique per URL. That convention is what lets Google and the AI engines deduplicate your business correctly across hundreds of crawled pages.
## Pick the deepest `LocalBusiness` subtype Schema.org offers for your trade
[Schema.org's `LocalBusiness`](https://schema.org/LocalBusiness) is the root type for any place-based business. Underneath it sits [`HomeAndConstructionBusiness`](https://schema.org/HomeAndConstructionBusiness), which is the branch trades businesses live on. Underneath that sit the named subtypes that match specific trades:
| Trade | Schema.org type | Inheritance chain |
|---|---|---|
| Roofing contractor | [`RoofingContractor`](https://schema.org/RoofingContractor) | LocalBusiness > HomeAndConstructionBusiness > RoofingContractor |
| Plumber | [`Plumber`](https://schema.org/Plumber) | LocalBusiness > HomeAndConstructionBusiness > Plumber |
| Electrician | [`Electrician`](https://schema.org/Electrician) | LocalBusiness > HomeAndConstructionBusiness > Electrician |
| HVAC | [`HVACBusiness`](https://schema.org/HVACBusiness) | LocalBusiness > HomeAndConstructionBusiness > HVACBusiness |
| General contractor | [`GeneralContractor`](https://schema.org/GeneralContractor) | LocalBusiness > HomeAndConstructionBusiness > GeneralContractor |
| Painter | [`HousePainter`](https://schema.org/HousePainter) | LocalBusiness > HomeAndConstructionBusiness > HousePainter |
| Moving company | [`MovingCompany`](https://schema.org/MovingCompany) | LocalBusiness > HomeAndConstructionBusiness > MovingCompany |
| Locksmith | [`Locksmith`](https://schema.org/Locksmith) | LocalBusiness > HomeAndConstructionBusiness > Locksmith |
A trade that doesn't have a named subtype (drywall, masonry, gutter specialists, foundation repair, etc.) inherits the next level up. Use `HomeAndConstructionBusiness` for those. Don't fall back further to bare `LocalBusiness`, and definitely don't use plain `Organization`. The whole point of the subtype hierarchy is the tighter topical signal it sends to crawlers.
The full `RoofingContractor` node, in production form:
```json
{
"@type": "RoofingContractor",
"@id": "https://watson-roofing.com/#org",
"name": "Watson Roofing Co.",
"image": "https://watson-roofing.com/images/logo.png",
"url": "https://watson-roofing.com",
"telephone": "+1-225-555-0100",
"address": {
"@type": "PostalAddress",
"streetAddress": "1234 Main Street",
"addressLocality": "Watson",
"addressRegion": "LA",
"postalCode": "70786",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 30.5089,
"longitude": -90.9123
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:00",
"closes": "18:00"
}
],
"areaServed": [
{ "@type": "City", "name": "Watson" },
{ "@type": "City", "name": "Baton Rouge" },
{ "@type": "City", "name": "Denham Springs" }
],
"priceRange": "$$",
"knowsAbout": [
"Asphalt shingle roof replacement",
"Metal roofing systems",
"Storm-damage insurance claims"
],
"sameAs": [
"https://g.page/r/CXXX...",
"https://www.facebook.com/watsonroofing",
"https://www.linkedin.com/company/watson-roofing",
"https://www.bbb.org/us/la/watson/profile/.../"
]
}
```
Schema.org's own description of what a `LocalBusiness` is, verbatim:
“
A particular physical business or branch of an organization. Examples of LocalBusiness include a restaurant, a particular branch of a restaurant chain, a branch of a bank, a medical practice, a club, a bowling alley, etc.
—Schema.org, official type definition for LocalBusiness
The phrase "particular physical business" is doing the work in that definition. A roofer with no fixed shopfront, who operates out of a garage and meets customers at the job site, is still a particular physical business with a verifiable address. That qualifies for `LocalBusiness` and the subtype hierarchy. A purely-remote consulting practice is not a `LocalBusiness`. Different schema scope. Different rich-results entitlements.
## The `@id` is the wire that connects every node in your graph
`@id` is the JSON-LD property that gives an entity a stable identifier. Other nodes in the graph reference that identifier instead of duplicating data, and crawlers across the web merge entities that share the same `@id`. Get the `@id` convention right and every page on your site reinforces a single coherent business entity. Get it wrong and you fragment your business into orphan nodes that Google and the AI engines can't connect.
Three rules cover the discipline:
1. **Use IRI form with a `#fragment`.** The `@id` should look like a URL: `https://watson-roofing.com/#org`. The `#org` fragment names the entity within the page. Common fragments: `#org`, `#website`, `#webpage`, `#article`, `#person`, `#service`, `#breadcrumbs`.
2. **Sitewide entities keep the same `@id` everywhere.** The `RoofingContractor` node on your homepage, your service pages, your about page, and every blog post all share `@id: "https://watson-roofing.com/#org"`. That's how the entity stays singular across the crawl.
3. **Page-specific entities include the page path.** A `WebPage` node uses `@id: "https://watson-roofing.com/services/replacement/#webpage"`. An `Article` node uses `@id: "https://watson-roofing.com/blog/roof-lifespan/#article"`. The page path makes each `@id` unique per URL.
Cross-references inside the graph point to `@id`s instead of repeating data:
```json
{
"@type": "Article",
"@id": "https://watson-roofing.com/blog/roof-lifespan/#article",
"headline": "How long does an asphalt roof actually last?",
"author": { "@id": "https://watson-roofing.com/team/mike-watson/#person" },
"publisher": { "@id": "https://watson-roofing.com/#org" },
"isPartOf": { "@id": "https://watson-roofing.com/blog/roof-lifespan/#webpage" }
}
```
Three references, zero duplicated data. The `Article` knows its author is the `Person` declared elsewhere in the graph, its publisher is the sitewide `RoofingContractor`, and it belongs to the `WebPage` declared in the same graph. The crawler walks those references and reconstructs the relationships without any guesswork.
## `Service` nodes — one per offering, linked back to your business
Every distinct service your business offers should emit its own [`Service`](https://schema.org/Service) node. A roofer offering replacement, repair, gutter installation, and emergency tarp service emits four `Service` nodes. Each one links back to the `RoofingContractor` via `provider`:
```json
{
"@type": "Service",
"@id": "https://watson-roofing.com/services/replacement/#service",
"serviceType": "Roof replacement",
"name": "Asphalt shingle roof replacement",
"description": "Full tear-off and replacement of asphalt shingle roofs in Greater Baton Rouge. Includes decking inspection, ice and water shield, GAF Timberline HDZ shingles, 50-year manufacturer warranty.",
"provider": { "@id": "https://watson-roofing.com/#org" },
"areaServed": [
{ "@type": "City", "name": "Watson" },
{ "@type": "City", "name": "Baton Rouge" }
],
"offers": {
"@type": "Offer",
"priceSpecification": {
"@type": "PriceSpecification",
"minPrice": 7500,
"maxPrice": 18000,
"priceCurrency": "USD"
}
}
}
```
The `provider` reference is the load-bearing part. Without it, the `Service` node is an orphan. With it, the AI engine knows that this specific roof-replacement offering is provided by the Watson Roofing entity declared in the same graph, which in turn has a verified address, a `sameAs` array linking to BBB and LinkedIn, and an `aggregateRating` from real Google reviews. The whole chain of credibility flows from the `Service` back to the `RoofingContractor` through the single `@id` reference.
`Service` schema also matters for AI Overviews specifically. When Google's AI Overview is summarizing options for "roof replacement Cleveland," it pulls candidate businesses by querying the entity graph for `RoofingContractor` (or subtype) entities with `Service` nodes whose `serviceType` matches the query. A roofer with four well-formed `Service` nodes shows up four ways. A roofer with no `Service` nodes at all only shows up through the parent business entity, which is a weaker match.
## `Person` + `worksFor` — how to schema-credit your crew and leadership
Trades businesses get a credibility boost from named, verifiable team members. The [`Person`](https://schema.org/Person) schema is how you give a crew member, a foreman, or a leadership team member their own entity in the graph. The relevant properties for a trades-business team member:
```json
{
"@type": "Person",
"@id": "https://watson-roofing.com/team/mike-watson/#person",
"name": "Mike Watson",
"jobTitle": "Owner & Lead Estimator",
"image": "https://watson-roofing.com/team/mike-watson.jpg",
"worksFor": { "@id": "https://watson-roofing.com/#org" },
"knowsAbout": [
"Insurance claim adjudication",
"Storm-damage assessment",
"Asphalt shingle systems"
],
"hasCredential": [
{
"@type": "EducationalOccupationalCredential",
"name": "GAF Master Elite Certified Contractor",
"credentialCategory": "Manufacturer certification"
}
],
"sameAs": [
"https://www.linkedin.com/in/mike-watson-roofing/",
"https://www.facebook.com/mike.watson.roofing"
]
}
```
Two properties are doing most of the work. `worksFor` links the person back to the business entity, so the crawler knows this is a team member and not an unrelated `Person`. `knowsAbout` lists the topics this person has expertise in, which the AI engine uses for topic matching when deciding whose content to cite.
The `hasCredential` array is the trades-specific power move. Master Elite from GAF, Owens Corning Platinum Preferred, NRCA membership, OSHA certifications, state contractor licenses, anything a customer or AI engine could independently verify. Each credential becomes a topical signal. A roofer whose owner has `hasCredential: ["GAF Master Elite"]` outranks a roofer with no credentials on queries where the AI is evaluating expertise depth.
One `Person` node per real team member who appears publicly on the site. Leadership goes first. Crew foremen who appear in case studies or guides come next. The whole roster doesn't need schema, just the named individuals the customer might evaluate.
## `Review` vs `Quotation` — the schema choice nobody gets right
The two schemas for embedding testimonials look interchangeable. They aren't. They feed different surfaces and serve different goals.
`Review` is Google's preferred type for the rich-results star-rating snippet in the SERP. It powers the in-result star display and feeds the `aggregateRating` calculation. The required properties are minimal but strict:
```json
{
"@type": "Review",
"@id": "https://watson-roofing.com/services/replacement/#review-sarah-m",
"itemReviewed": { "@id": "https://watson-roofing.com/#org" },
"author": { "@type": "Person", "name": "Sarah M." },
"datePublished": "2026-04-12",
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5",
"worstRating": "1"
},
"reviewBody": "Watson Roofing replaced our roof in two days. The crew showed up at 7am sharp, cleaned up everything before leaving, and we came in $400 under the original estimate."
}
```
[`Quotation`](https://schema.org/Quotation) is the schema AI engines reach for when they're pulling verbatim text into a recommendation answer. It carries the quote text, the creator (the person who said it), and a `citation` link back to the verifiable source. Schema.org's intent for `Quotation` is broader than testimonials, but it's the right type for any attributable verbatim text you want an AI to extract:
```json
{
"@type": "Quotation",
"text": "Watson Roofing replaced our roof in two days. The crew showed up at 7am sharp, cleaned up everything before leaving, and we came in $400 under the original estimate.",
"creator": { "@type": "Person", "name": "Sarah M." },
"citation": "https://g.page/r/CXXX..."
}
```
For a high-value testimonial that you want both ways (SERP star snippet + AI citation), emit both. They don't conflict, they reference the same underlying quote text, and the dual surface increases your odds of getting picked up in either context. For routine reviews you're displaying in a slider widget, `Review` alone is usually enough.
## Where `aggregateRating` actually goes, and where it doesn't
[`AggregateRating`](https://schema.org/AggregateRating) is the property most trades sites deploy hoping for star displays in Google results, so set expectations honestly first: since Google's 2019 self-serving-reviews update, review stars no longer display for `LocalBusiness` or `Organization` markup about your own business, no matter how clean the markup is. The property still earns its place. A well-formed rating block that public platforms corroborate feeds the entity-confidence layer AI engines use when they cross-check whether a business is what its schema claims. It just doesn't buy SERP stars anymore, and anyone promising otherwise is selling you 2018.
Placement still matters for correctness. `aggregateRating` belongs on the `LocalBusiness` subtype node (your `RoofingContractor` or `Plumber`), not on a bare `Organization`. If your `RoofingContractor` and `Organization` share the same `@id` (the correct setup for a trades business), the rating attached to the subtype declaration is what validators and parsers read.
The correct placement:
```json
{
"@type": "RoofingContractor",
"@id": "https://watson-roofing.com/#org",
"name": "Watson Roofing Co.",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": 132,
"bestRating": "5",
"worstRating": "1"
}
}
```
A few rules that protect the rating from getting denied or marked invalid:
Do this
Source aggregateRating from reviews you actually collect and display on your own site, paired with visible Review or Quotation content a reader can see. Keep the numbers consistent with what a customer would find on your public profiles; if your site claims 4.9 from 132 reviews and your Google Business Profile shows something different, that mismatch is an integrity signal working against you.
Use reviewCount (the integer total) plus ratingValue (the average to one decimal). Don't fabricate. Google's spam team catches mismatched aggregates at scale, and an enforcement action removes your rich-results eligibility across the entire site, not just the one page.
Avoid
aggregateRating copied or summed from third-party platforms (Google reviews, BBB) into your own markup. Google's review-snippet guidance requires ratings to come from reviews collected by the site itself; importing platform numbers into self-markup violates it.
Counting on stars in the SERP. Self-serving review markup has been excluded from star display since 2019; treat the property as entity corroboration, not a rich-result play.
## The page-level nodes: `BreadcrumbList`, `WebPage`, `Article`
Beyond the business-level entities, every page in your graph needs page-level nodes. Three types cover most cases.
**`BreadcrumbList`** describes the navigation path to the current page. It powers the visible breadcrumb trail in Google search results. The node lists the path items in order:
```json
{
"@type": "BreadcrumbList",
"@id": "https://watson-roofing.com/services/replacement/#breadcrumbs",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://watson-roofing.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Services",
"item": "https://watson-roofing.com/services/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Roof Replacement",
"item": "https://watson-roofing.com/services/replacement/"
}
]
}
```
**`WebPage`** is the wrapper for the current URL. It connects to the sitewide `WebSite` via `isPartOf` and points at the main content entity (a `Service`, `Article`, or whatever the page is primarily about) via `about` or `mainEntity`:
```json
{
"@type": "WebPage",
"@id": "https://watson-roofing.com/services/replacement/#webpage",
"url": "https://watson-roofing.com/services/replacement/",
"name": "Roof Replacement in Watson, LA",
"isPartOf": { "@id": "https://watson-roofing.com/#website" },
"about": { "@id": "https://watson-roofing.com/services/replacement/#service" },
"breadcrumb": { "@id": "https://watson-roofing.com/services/replacement/#breadcrumbs" },
"datePublished": "2026-05-28",
"dateModified": "2026-05-28"
}
```
**`Article`** (or `BlogPosting`) applies to long-form content like guides and blog posts. It carries the headline, byline, publish date, modification date, and content references:
```json
{
"@type": "Article",
"@id": "https://watson-roofing.com/blog/roof-lifespan/#article",
"headline": "How long does an asphalt roof actually last in Louisiana?",
"author": { "@id": "https://watson-roofing.com/team/mike-watson/#person" },
"publisher": { "@id": "https://watson-roofing.com/#org" },
"datePublished": "2026-05-15",
"dateModified": "2026-05-15",
"mainEntityOfPage": { "@id": "https://watson-roofing.com/blog/roof-lifespan/#webpage" }
}
```
For pages that contain a Q&A section, add [`FAQPage`](https://schema.org/FAQPage). For step-by-step how-to content, add [`HowTo`](https://schema.org/HowTo). Both are well-documented in Google Search Central, both unlock real rich-results entitlements, and both are extracted aggressively by AI engines for citation answers.
Expand: a complete trades-business graph for a single service page
```json
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "RoofingContractor",
"@id": "https://watson-roofing.com/#org",
"name": "Watson Roofing Co.",
"image": "https://watson-roofing.com/images/logo.png",
"url": "https://watson-roofing.com",
"telephone": "+1-225-555-0100",
"address": {
"@type": "PostalAddress",
"streetAddress": "1234 Main Street",
"addressLocality": "Watson",
"addressRegion": "LA",
"postalCode": "70786",
"addressCountry": "US"
},
"geo": { "@type": "GeoCoordinates", "latitude": 30.5089, "longitude": -90.9123 },
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:00",
"closes": "18:00"
}
],
"areaServed": [
{ "@type": "City", "name": "Watson" },
{ "@type": "City", "name": "Baton Rouge" }
],
"priceRange": "$$",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": 132,
"bestRating": "5",
"worstRating": "1"
},
"sameAs": [
"https://g.page/r/CXXX...",
"https://www.facebook.com/watsonroofing",
"https://www.linkedin.com/company/watson-roofing",
"https://www.bbb.org/us/la/watson/profile/.../"
]
},
{
"@type": "WebSite",
"@id": "https://watson-roofing.com/#website",
"url": "https://watson-roofing.com",
"publisher": { "@id": "https://watson-roofing.com/#org" }
},
{
"@type": "WebPage",
"@id": "https://watson-roofing.com/services/replacement/#webpage",
"url": "https://watson-roofing.com/services/replacement/",
"name": "Roof Replacement in Watson, LA",
"isPartOf": { "@id": "https://watson-roofing.com/#website" },
"about": { "@id": "https://watson-roofing.com/services/replacement/#service" },
"breadcrumb": { "@id": "https://watson-roofing.com/services/replacement/#breadcrumbs" }
},
{
"@type": "BreadcrumbList",
"@id": "https://watson-roofing.com/services/replacement/#breadcrumbs",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://watson-roofing.com/" },
{ "@type": "ListItem", "position": 2, "name": "Services", "item": "https://watson-roofing.com/services/" },
{ "@type": "ListItem", "position": 3, "name": "Roof Replacement", "item": "https://watson-roofing.com/services/replacement/" }
]
},
{
"@type": "Service",
"@id": "https://watson-roofing.com/services/replacement/#service",
"serviceType": "Roof replacement",
"name": "Asphalt shingle roof replacement",
"description": "Full tear-off and replacement of asphalt shingle roofs in Greater Baton Rouge.",
"provider": { "@id": "https://watson-roofing.com/#org" },
"areaServed": [
{ "@type": "City", "name": "Watson" },
{ "@type": "City", "name": "Baton Rouge" }
],
"offers": {
"@type": "Offer",
"priceSpecification": {
"@type": "PriceSpecification",
"minPrice": 7500,
"maxPrice": 18000,
"priceCurrency": "USD"
}
}
}
]
}
```
This is the production shape. Substitute your business name, address, geo coordinates, services, and reviews. Validate with the Rich Results Test before deploying.
## Validation is non-negotiable. Three tools, in this order.
A JSON-LD block that looks correct can still have silent errors that deny rich-results eligibility. Validate every page before deploying schema changes. Three tools, used in sequence:
**1. [Schema Markup Validator](https://validator.schema.org/)** is Schema.org's own validator. It catches structural errors (malformed JSON, missing required properties for a given type, wrong data types). Paste the page URL or the raw JSON-LD, and it returns a tree view of your graph plus a list of errors and warnings. Fix everything in the error list. The warnings are optional.
**2. [Google Rich Results Test](https://search.google.com/test/rich-results)** validates against Google's specific rich-results requirements, which are stricter than the underlying Schema.org spec. It tells you which rich-result entitlements your page qualifies for (review snippet, FAQ rich result, breadcrumb display, etc.) and surfaces errors that would deny those entitlements. Fix every error. Several warnings are okay. Some warnings indicate genuinely optional improvements; others (especially around missing recommended properties) are silent rich-results gates.
**3. View the rendered JSON-LD in the live page source.** Browser DevTools is fine. The point is to read the `