AI visibility for small sites
A solo founder or small team who wants ChatGPT / Perplexity / Claude /
Google AI Overviews to actually mention their business by name when
buyers ask relevant questions. The lever is structured data (JSON-LD
the crawlers actually read) plus answer-ready content (the Q&A shape
the models quote).
This is not enterprise SEO. No sitemaps, no hreflang, no log
files. The output of this skill is a 90-minute build that closes the
two largest visibility gaps a small business has on AI search.
When to load this
Load when any of these signals fire:
- The user shows a Stripe / HubSpot / Ahrefs "AI visibility audit"
report saying "AI says nothing specific about you" or "vague ·
hesitant · forgettable."
- The user says "I want to be cited by ChatGPT" or "show up in
Perplexity" or "Google AI Overviews is sending me no traffic."
- The user asks about "schema.org," "JSON-LD," "structured data,"
"FAQ rich snippets," or "rich results."
- The user has a static / GitHub-Pages / low-CMS site and wants to
win organic discovery in 2026.
- A session review surfaces that AI models cannot answer basic
questions about the user's business.
- The user mentions Stripe / HubSpot / Ahrefs / an "AI audit" or
"AI visibility" report by name, or quotes language like
"vague, hesitant, forgettable" / "what ChatGPT says about you"
/ "buyer prompts you don't show up for." This is the most
common trigger shape.
When NOT to load this
- The user has a multi-thousand-page site with technical SEO
problems. Hand them off to a real SEO consultant.
- The user wants to game AI search with low-quality content farms.
Do not help. Cite this skill's name and decline.
- The user wants paid ads in AI search. Out of scope for this skill;
route to a paid-acquisition playbook.
The 4-gaps framing
When an audit shows "AI says nothing about you," it is almost always
one of these four gaps, in this priority order:
- Answer-ready content for buyer prompts. You do not have a page
that directly answers the questions buyers ask AI. Fix this first
— biggest ROI, you control it today.
- Clear, structured brand entity. AI models need a clean
Organization (or LocalBusiness) block with name, founder,
location, contact, logo, and same-as links. Without this, models
hedge with "a company in its space."
- Authoritative third-party citations. Your business is named on
other sites (Skool, podcast guest bios, Reddit, news coverage).
AI models use these to corroborate identity.
- Reputation & review signals.
AggregateRating, individual
Review blocks, third-party review-site presence. Only add these
if you have real reviews — do not fabricate.
The Stripe / HubSpot audits name these four gaps in exactly this
order. The 90-minute build below closes #1 and #2 fully and partly
#3. #4 is ongoing.
The 90-minute build
Three artifacts. All JSON-LD (not Microdata, not RDFa — JSON-LD is
the modern default and what every major AI crawler prefers).
Artifact 1: Organization + LocalBusiness + Service × N — homepage head injection
One <script type="application/ld+json"> block. Drops into the
homepage's <head>. Zero visible HTML changes. Structure:
{
"@context": "https://schema.org",
"@graph": [
{ "@type": ["Organization", "LocalBusiness"],
"@id": "https://example.com/#organization",
"name": "...", "url": "...",
"logo": "...", "image": "...",
"description": "...",
"founder": { "@type": "Person", "name": "...", "email": "..." },
"contactPoint": [{ "@type": "ContactPoint", "contactType": "customer support", "email": "..." }],
"sameAs": [ "https://skool.com/...", "https://twitter.com/..." ],
"address": { "@type": "PostalAddress", "addressCountry": "US" } },
{ "@type": "Service", "name": "...", "provider": { "@id": "#organization" },
"offers": { "@type": "Offer", "price": "149.00", "priceCurrency": "USD",
"availability": "https://schema.org/InStock" } }
]
}
For a business with N priced products, emit N Service blocks. The
price, currency, and availability are the highest-leverage fields —
AI models quote them in recommendations.
Pitfalls:
@type can be a string or an array. Use the array form
["Organization", "LocalBusiness"] to inherit both type hierarchies.
This is the single most common schema.org gotcha.
- Provider must be a reference, not a copy. Use
"provider": { "@id": "#organization" } to point to the entity in @graph.
Copying the data instead of referencing breaks the entity in
crawlers' knowledge graphs.
- Do not fabricate reviews / ratings. If you have none, omit
aggregateRating entirely. Lying here is a policy violation and
erodes trust in everything else on the page.
- Currency precision.
"price": "149.00" not "149". The
decimal is required by Google's Rich Results Test even if it's
zero.
Artifact 2: A new /faq/ page with FAQPage JSON-LD
The single biggest AI-citation win. Direct Q&A for every buyer
prompt surfaced in the audit. Visible HTML uses <details> /
<summary> for collapsibility (good UX, good a11y); the same Q&A
appears in the JSON-LD mainEntity array.
Shape:
{ "@context": "https://schema.org", "@type": "FAQPage",
"mainEntity": [
{ "@type": "Question",
"name": "How can I...?",
"acceptedAnswer": { "@type": "Answer", "text": "..." } }
] }
Target 7–12 questions per page. Use the buyer's exact phrasing
from the audit as the name field — models match on those tokens.
For the visible HTML, render the same Q&A in <details> blocks
so the page works for sighted users too. A page that is JSON-LD
without visible content looks like a link scheme to Google.
Artifact 3: A short README.md with validate + deploy steps
The user is going to deploy this. They need: how to validate (URLs
to Google Rich Results Test and Schema Markup Validator), how to
paste (which file, which line), how to deploy (git commands), how to
know it worked (Search Console FAQ detection, expected timing
3–7 days for Google, longer for AI crawlers).
The "answer-ready content" plan (gap #1)
The schema.org scaffolding without matching content is a hollow
shell. The content plan is the other half of gap #1:
For each of the 7 buyer prompts the audit surfaced, the FAQ page
must:
- Use the prompt's exact wording (or close paraphrase) as a
<h2>
or <details> summary.
- Answer in plain, specific prose — 80–200 words per answer. No
marketing fluff, no "we are the best," no unbacked superlatives.
AI models downweight promotional language in citations.
- Reference concrete facts the user already said in conversation:
prices, year founded, founder name, device compatibility,
proof points, customer profile. Specifics are what make the
answer quotable.
- Include 1–2 honest alternatives for context ("you have three
paths, here is option 2 and 3"). This makes the answer feel
editorial rather than promotional, which models reward.
The "older adult memoir writer" prompt from the Offline Helper
audit is a textbook example: a 68-year-old first-time memoir
writer who drafts in 45-minute sessions using the AI as a quiet,
non-judgmental first reader. That sentence is quotable because it
is specific, true, and was given by the user unprompted. Use that
kind of material.
Citations to start chasing (gap #3)
After the build ships, the highest-leverage citations for a small
service business are:
- Skool / Circle / Mighty Networks community page with the
founder's name and a real description.
- Guest podcast episodes (founders and operators talking about
their niche). Add as
sameAs on the Organization block.
- Hacker News / Reddit / Indie Hackers mentions — public,
crawled, not removable.
- A short founder bio page on a personal site with
sameAs
links to LinkedIn, GitHub, etc.
You will not turn these around in a week. But adding them as
sameAs on the Organization block is free, takes 5 minutes per
citation, and compounds over months.
Validate before ship — non-negotiable
The single biggest pitfall when writing JSON-LD by hand: shipping
broken syntax. This session's FAQ page shipped with a missing
"acceptedAnswer": { wrapper on a Q, only caught by re-parsing
with a JSON loader. The fix is mechanical and mandatory:
import json, re
text = open("path/to/page.html").read()
for block in re.findall(r'<script type="application/ld\+json">(.*?)</script>',
text, re.DOTALL):
json.loads(block) # raises on syntax error
print("OK")
Always run this on every HTML file you write or modify that
contains a JSON-LD block. Before declaring done. Every time.
Also validate against Google's expected-field rules:
Organization requires: @type, name, url, logo.
Service requires: @type, name, provider, offers.
FAQPage requires: @type, mainEntity (non-empty array).
Question requires: @type, name, acceptedAnswer.
Answer requires: @type, text.
Offer requires: @type, price, priceCurrency.
https://search.google.com/test/rich-results catches missing
fields; https://validator.schema.org/ catches syntax.
Match the design system
The FAQ page should not look like a JSON-LD test stub. Match the
homepage's CSS variables (colors, font, spacing, radius) so the
new page looks native. Read the homepage <style> block first
and copy the same :root custom properties into the new page.
Spending 20 minutes on visual match is not optional polish — it is
the difference between "real business" and "SEO experiment." Google
and AI models both downweight pages that look auto-generated.
Pitfalls
- Shipping JSON-LD without re-parsing it. Caught me in this
session. Always validate before declaring done.
- "Done" ≠ deployed. This is the single most embarrassing
failure mode in this skill: you build the FAQ page, validate
the JSON-LD, screenshot it locally, write a great README, and
report "deliverable shipped." The user then asks
"Everything is committed to GitHub and live on the site?" and
you discover the files are sitting untracked in the local repo
and the live URL still 404s. The fix is mechanical: after
every AI-visibility build, run the full ship sequence —
git add, git commit, git push, then curl -I the live
URL and grep the served HTML for application/ld+json before
you tell the user it's done. Local validation, screenshot
proof, and "I wrote a README" are not the same as "it's live."
See "Deployment discipline" below.
- Forgetting the visible HTML behind the JSON-LD. A page that
is JSON-LD-only looks like a link scheme. Always have a
human-readable version of the same Q&A.
- Fabricating
aggregateRating or Review data. Policy
violation. Omit entirely if you have no real reviews.
- Promotional language in FAQ answers. Models downweight
"we are the leading provider" type prose. Specific facts, named
proof points, and editorial alternatives are what get cited.
- Updating the catalog without updating the JSON-LD
Offer
block. Pricing change = edit two places. Document both in
the README so a future maintainer does not break one.
- Confusing the Stripe pricing-table ID with a price ID.
A
<stripe-pricing-table pricing-table-id="prctbl_…"> is
a container that holds multiple products; the prctbl_…
ID is not usable in line_items[].price. The Worker (or
any custom Checkout integration) needs each product's
individual price_… ID, not the container. Same shape
applies to CSV product imports, which return a
v2.commerce.product_catalog_import object with a
status: "awaiting_upload" and an upload_url — useful
internally to Stripe, not an artifact your integration
consumes. Always ask for the individual price_… IDs
when you need them, and verify the price_… IDs the
integration uses by re-fetching the deployed bundle
(GET /accounts/<id>/workers/scripts/<name> from the
Cloudflare API returns the raw source) before declaring
the catalog wired.
- Setting
price: "149" (no decimal). The decimal is
technically required by the schema, even when zero. Always
"149.00".
- Forgetting
availability: "https://schema.org/InStock".
AI models surface availability in recommendations. Omitting it
leaves the field ambiguous and downranks the listing.
- Treating AI visibility as one-and-done. It is not. Models
re-crawl on a lag of days to weeks. Re-validate monthly. Add
citations as you earn them. Update the FAQ as buyers ask new
questions.
Deployment discipline
The build is not the ship. The full ship sequence for any
AI-visibility deliverable:
- Local validation —
python scripts/validate-json-ld.py path/to/page.html
reports 0 issues.
- Visual smoke test —
python3 -m http.server locally, open the
page in the browser, confirm the visible HTML matches the
JSON-LD content (no <details> mismatches, no broken
design-system inheritance).
- Git —
git add, git commit -m "<message that mentions AI visibility>", git push origin <branch>. Confirm the push
landed: git log --oneline origin/<branch> -1.
- Wait for deploy — GitHub Pages typically redeploys in
30–90 seconds. Poll with
curl -I <live-url> until the
Last-Modified or ETag advances.
- Verify the live HTML —
curl -s <live-url> | grep -c 'application/ld+json' should match the count of JSON-LD
blocks you wrote. curl -s <live-url> | grep -oE '"@type": "[^"]+"' | sort -u should list every type you wrote.
- Only then say "live." Anything before step 6 is "built and
ready to ship" — not the same claim.
If the user has to come back and ask "is it actually live?," you
broke this discipline. Apologize briefly, finish the ship, and
re-check your mental model of what "done" means in this skill.
Linked files
references/json-ld-snippets.md — copy-paste-ready JSON-LD
blocks for the most common small-business shapes (solo founder
service, multi-product shop, local business, B2B SaaS).
references/policy-page-templates.md — the three pages
(Terms of Service, Privacy Policy, Refund Policy) that Stripe,
Google Play, Apple App Store, and most payment processors
require before they will let a small business charge cards. AI
crawlers also use them as authoritative corpus. Includes the
"short version" callout pattern and the "not legal advice"
footer language.
templates/faq-page.html — full /faq/index.html skeleton
matching the Offline Helper warm-paper design, ready to adapt
to any brand by swapping the :root custom properties and
the mainEntity Q&A list.
scripts/validate-json-ld.py — the re-parse loop, plus a
required-field check, runnable as a single command.
Policy pages — a hidden AI-visibility + payment-prerequisite gap
The same Stripe / HubSpot / Ahrefs audits that flag "AI says
nothing about you" almost always also flag (or worse, don't
flag and then block go-live) a separate gap: no Terms, no
Privacy, no Refund policy at real URLs on the business's own
domain. Two consequences:
- Stripe will block go-live. The Live-mode onboarding form
has a "Business settings" step with three URL fields that
default to
https://example.com/.... Saving those placeholders
is technically allowed but produces a real legal exposure:
every receipt and Checkout page will link to Stripe's generic
docs at example.com/terms and a privacy policy that does not
exist. Multiple US states (CA, VA, CO, CT, UT, …) require a
real, accessible privacy policy before a business can collect
payment data. Fix: ship the three pages on the business
domain before pasting the URLs into Stripe.
- AI crawlers use the policies as authoritative corpus. When
ChatGPT or Perplexity is asked "is this business legit / what
is their refund policy," the model often quotes the business's
own policy page verbatim. Missing policies are a real
citation loss, not just a legal risk.
The three pages take ~90 minutes to write. Each is plain English,
US-law-aware, and matches the rest of the site's design system.
See references/policy-page-templates.md for the templates and
the "short version / not legal advice" callout pattern that
lands well with both regulators and AI models.
When you only have 90 minutes — the quick-build order
The full playbook above is comprehensive. When the user is in
a hurry (a launch tomorrow, a press cycle, a Stripe audit they
just discovered) and only has ~90 minutes, ship only the two
code-only gaps in this order and defer the rest:
- Identify 5–7 ready-to-buy buyer prompts from the audit.
Group by intent (research vs comparison vs ready-to-buy) and
prioritize the ready-to-buy ones first — those are the
prompts where a citation directly converts.
- Write the answers in human prose first. 50–150 words
each, plain-spoken, mentioning the product by name, linking
to the relevant product/fit-check page. AI models quote these
answers verbatim; bad prose = bad citations.
- Wrap them in
FAQPage JSON-LD. New /faq/ page. Test
that every block parses with json.loads (no trailing
commas, no single quotes) before commit.
- Build the
Organization + Service blocks for the
homepage. One <script type="application/ld+json"> block,
multiple entities combined via @graph.
- Validate, ship, verify live. Schema Markup Validator
- Google Rich Results Test →
git add/commit/push →
curl -I the live URL → grep the served HTML for
application/ld+json → only then report "live."
Gaps 3 (citations) and 4 (reputation) take months of work and
are not in scope for the quick build. The user will need a
follow-up to chase Skool/podcast/Reddit mentions and to start
collecting real reviews. The ai-visibility-build skill was
the previous quick-build-only skill; it has been consolidated
into this one — the full playbook above plus the prioritized
quick-build order here is the new canonical home.
Schema.org type selection — quick reference
| Site shape |
Types to emit |
| Single-product / single-service |
Organization + Service (with Offer) + FAQPage |
| Multi-product catalog with prices |
Organization + 1× Service per product (each with its own Offer) + FAQPage |
| E-commerce with multiple SKUs |
Organization + multiple Product (each with Offer) — use Product, not Service, when the customer is buying a thing, not a session |
| Subscription product |
Service + Offer with priceSpecification: { @type: UnitPriceSpecification, unitText: "MONTH" } |
| Free trial + paid |
Two Offer blocks (one with price: "0", one with the paid price) OR a single Offer with priceSpecification and a subscription_data.trial_period_days reference in the description |
1---2name: ai-visibility-for-small-sites3description: Make a small business website (solo founder, local service, niche SaaS, personal brand) discoverable and citable by AI search — ChatGPT, Perplexity, Claude, Gemini, Google AI Overviews. Covers the schema.org JSON-LD scaffolding (Organization, LocalBusiness, Service, Offer, FAQPage), the "answer-ready content for buyer prompts" plan, the citation/reputation gap closer, and the validate-before-ship discipline (always re-parse every JSON-LD block you write). Load when the user asks about AI visibility, GEO (generative engine optimization), being cited by ChatGPT, schema.org / structured data, FAQ rich snippets, or wants to close the "AI says nothing about my business" gap. Skip for large enterprise SEO (technical sitemaps, hreflang, log-file analysis) — this is the small-team, no-Specialist playbook.4---56# AI visibility for small sites78A solo founder or small team who wants ChatGPT / Perplexity / Claude /9Google AI Overviews to actually mention their business by name when10buyers ask relevant questions. The lever is structured data (JSON-LD11the crawlers actually read) plus answer-ready content (the Q&A shape12the models quote).1314This is **not** enterprise SEO. No sitemaps, no hreflang, no log15files. The output of this skill is a 90-minute build that closes the16two largest visibility gaps a small business has on AI search.1718## When to load this1920Load when **any** of these signals fire:2122- The user shows a Stripe / HubSpot / Ahrefs "AI visibility audit"23 report saying "AI says nothing specific about you" or "vague ·24 hesitant · forgettable."25- The user says "I want to be cited by ChatGPT" or "show up in26 Perplexity" or "Google AI Overviews is sending me no traffic."27- The user asks about "schema.org," "JSON-LD," "structured data,"28 "FAQ rich snippets," or "rich results."29- The user has a static / GitHub-Pages / low-CMS site and wants to30 win organic discovery in 2026.31- A session review surfaces that AI models cannot answer basic32 questions about the user's business.33- The user mentions Stripe / HubSpot / Ahrefs / an "AI audit" or34 "AI visibility" report by name, or quotes language like35 "vague, hesitant, forgettable" / "what ChatGPT says about you"36 / "buyer prompts you don't show up for." This is the most37 common trigger shape.3839## When NOT to load this4041- The user has a multi-thousand-page site with technical SEO42 problems. Hand them off to a real SEO consultant.43- The user wants to game AI search with low-quality content farms.44 Do not help. Cite this skill's name and decline.45- The user wants paid ads in AI search. Out of scope for this skill;46 route to a paid-acquisition playbook.4748## The 4-gaps framing4950When an audit shows "AI says nothing about you," it is almost always51one of these four gaps, in this priority order:52531. **Answer-ready content for buyer prompts.** You do not have a page54 that directly answers the questions buyers ask AI. Fix this first55 — biggest ROI, you control it today.562. **Clear, structured brand entity.** AI models need a clean57 `Organization` (or `LocalBusiness`) block with name, founder,58 location, contact, logo, and same-as links. Without this, models59 hedge with "a company in its space."603. **Authoritative third-party citations.** Your business is named on61 other sites (Skool, podcast guest bios, Reddit, news coverage).62 AI models use these to corroborate identity.634. **Reputation & review signals.** `AggregateRating`, individual64 `Review` blocks, third-party review-site presence. Only add these65 if you have *real* reviews — do not fabricate.6667The Stripe / HubSpot audits name these four gaps in exactly this68order. The 90-minute build below closes #1 and #2 fully and partly69#3. #4 is ongoing.7071## The 90-minute build7273Three artifacts. All JSON-LD (not Microdata, not RDFa — JSON-LD is74the modern default and what every major AI crawler prefers).7576### Artifact 1: `Organization` + `LocalBusiness` + `Service` × N — homepage head injection7778One `<script type="application/ld+json">` block. Drops into the79homepage's `<head>`. Zero visible HTML changes. Structure:8081```json82{83 "@context": "https://schema.org",84 "@graph": [85 { "@type": ["Organization", "LocalBusiness"],86 "@id": "https://example.com/#organization",87 "name": "...", "url": "...",88 "logo": "...", "image": "...",89 "description": "...",90 "founder": { "@type": "Person", "name": "...", "email": "..." },91 "contactPoint": [{ "@type": "ContactPoint", "contactType": "customer support", "email": "..." }],92 "sameAs": [ "https://skool.com/...", "https://twitter.com/..." ],93 "address": { "@type": "PostalAddress", "addressCountry": "US" } },94 { "@type": "Service", "name": "...", "provider": { "@id": "#organization" },95 "offers": { "@type": "Offer", "price": "149.00", "priceCurrency": "USD",96 "availability": "https://schema.org/InStock" } }97 ]98}99```100101For a business with N priced products, emit N `Service` blocks. The102price, currency, and availability are the highest-leverage fields —103AI models quote them in recommendations.104105Pitfalls:106- **`@type` can be a string or an array.** Use the array form107 `["Organization", "LocalBusiness"]` to inherit both type hierarchies.108 This is the single most common schema.org gotcha.109- **Provider must be a reference, not a copy.** Use `"provider":110 { "@id": "#organization" }` to point to the entity in `@graph`.111 Copying the data instead of referencing breaks the entity in112 crawlers' knowledge graphs.113- **Do not fabricate reviews / ratings.** If you have none, omit114 `aggregateRating` entirely. Lying here is a policy violation and115 erodes trust in everything else on the page.116- **Currency precision.** `"price": "149.00"` not `"149"`. The117 decimal is required by Google's Rich Results Test even if it's118 zero.119120### Artifact 2: A new `/faq/` page with `FAQPage` JSON-LD121122The single biggest AI-citation win. Direct Q&A for every buyer123prompt surfaced in the audit. Visible HTML uses `<details>` /124`<summary>` for collapsibility (good UX, good a11y); the same Q&A125appears in the JSON-LD `mainEntity` array.126127Shape:128```json129{ "@context": "https://schema.org", "@type": "FAQPage",130 "mainEntity": [131 { "@type": "Question",132 "name": "How can I...?",133 "acceptedAnswer": { "@type": "Answer", "text": "..." } }134 ] }135```136137Target 7–12 questions per page. Use the buyer's *exact phrasing*138from the audit as the `name` field — models match on those tokens.139140For the visible HTML, render the same Q&A in `<details>` blocks141so the page works for sighted users too. A page that is JSON-LD142without visible content looks like a link scheme to Google.143144### Artifact 3: A short `README.md` with validate + deploy steps145146The user is going to deploy this. They need: how to validate (URLs147to Google Rich Results Test and Schema Markup Validator), how to148paste (which file, which line), how to deploy (git commands), how to149know it worked (Search Console FAQ detection, expected timing1503–7 days for Google, longer for AI crawlers).151152## The "answer-ready content" plan (gap #1)153154The schema.org scaffolding without matching content is a hollow155shell. The content plan is the other half of gap #1:156157For each of the 7 buyer prompts the audit surfaced, the FAQ page158must:159- Use the prompt's exact wording (or close paraphrase) as a `<h2>`160 or `<details>` summary.161- Answer in **plain, specific prose** — 80–200 words per answer. No162 marketing fluff, no "we are the best," no unbacked superlatives.163 AI models downweight promotional language in citations.164- Reference *concrete facts* the user already said in conversation:165 prices, year founded, founder name, device compatibility,166 proof points, customer profile. Specifics are what make the167 answer quotable.168- Include 1–2 honest alternatives for context ("you have three169 paths, here is option 2 and 3"). This makes the answer feel170 editorial rather than promotional, which models reward.171172The "older adult memoir writer" prompt from the Offline Helper173audit is a textbook example: a 68-year-old first-time memoir174writer who drafts in 45-minute sessions using the AI as a quiet,175non-judgmental first reader. That sentence is quotable because it176is specific, true, and was given by the user unprompted. Use that177kind of material.178179## Citations to start chasing (gap #3)180181After the build ships, the highest-leverage citations for a small182service business are:183184- **Skool / Circle / Mighty Networks community page** with the185 founder's name and a real description.186- **Guest podcast episodes** (founders and operators talking about187 their niche). Add as `sameAs` on the Organization block.188- **Hacker News / Reddit / Indie Hackers mentions** — public,189 crawled, not removable.190- **A short founder bio page on a personal site** with `sameAs`191 links to LinkedIn, GitHub, etc.192193You will not turn these around in a week. But adding them as194`sameAs` on the Organization block is free, takes 5 minutes per195citation, and compounds over months.196197## Validate before ship — non-negotiable198199The single biggest pitfall when writing JSON-LD by hand: shipping200broken syntax. This session's FAQ page shipped with a missing201`"acceptedAnswer": {` wrapper on a Q, only caught by re-parsing202with a JSON loader. The fix is mechanical and mandatory:203204```python205import json, re206text = open("path/to/page.html").read()207for block in re.findall(r'<script type="application/ld\+json">(.*?)</script>',208 text, re.DOTALL):209 json.loads(block) # raises on syntax error210 print("OK")211```212213**Always run this on every HTML file you write or modify that214contains a JSON-LD block. Before declaring done. Every time.**215216Also validate against Google's expected-field rules:217- `Organization` requires: `@type`, `name`, `url`, `logo`.218- `Service` requires: `@type`, `name`, `provider`, `offers`.219- `FAQPage` requires: `@type`, `mainEntity` (non-empty array).220- `Question` requires: `@type`, `name`, `acceptedAnswer`.221- `Answer` requires: `@type`, `text`.222- `Offer` requires: `@type`, `price`, `priceCurrency`.223224`https://search.google.com/test/rich-results` catches missing225fields; `https://validator.schema.org/` catches syntax.226227## Match the design system228229The FAQ page should not look like a JSON-LD test stub. Match the230homepage's CSS variables (colors, font, spacing, radius) so the231new page looks native. Read the homepage `<style>` block first232and copy the same `:root` custom properties into the new page.233234Spending 20 minutes on visual match is not optional polish — it is235the difference between "real business" and "SEO experiment." Google236and AI models both downweight pages that look auto-generated.237238## Pitfalls239240- **Shipping JSON-LD without re-parsing it.** Caught me in this241 session. Always validate before declaring done.242- **"Done" ≠ deployed.** This is the single most embarrassing243 failure mode in this skill: you build the FAQ page, validate244 the JSON-LD, screenshot it locally, write a great README, and245 report "deliverable shipped." The user then asks246 *"Everything is committed to GitHub and live on the site?"* and247 you discover the files are sitting untracked in the local repo248 and the live URL still 404s. The fix is mechanical: **after249 every AI-visibility build, run the full ship sequence —250 `git add`, `git commit`, `git push`, then `curl -I` the live251 URL and grep the served HTML for `application/ld+json` before252 you tell the user it's done.** Local validation, screenshot253 proof, and "I wrote a README" are not the same as "it's live."254 See "Deployment discipline" below.255- **Forgetting the visible HTML behind the JSON-LD.** A page that256 is JSON-LD-only looks like a link scheme. Always have a257 human-readable version of the same Q&A.258- **Fabricating `aggregateRating` or `Review` data.** Policy259 violation. Omit entirely if you have no real reviews.260- **Promotional language in FAQ answers.** Models downweight261 "we are the leading provider" type prose. Specific facts, named262 proof points, and editorial alternatives are what get cited.263- **Updating the catalog without updating the JSON-LD `Offer`264 block.** Pricing change = edit two places. Document both in265 the README so a future maintainer does not break one.266- **Confusing the Stripe pricing-table ID with a price ID.**267 A `<stripe-pricing-table pricing-table-id="prctbl_…">` is268 a *container* that holds multiple products; the `prctbl_…`269 ID is not usable in `line_items[].price`. The Worker (or270 any custom Checkout integration) needs each product's271 individual `price_…` ID, not the container. Same shape272 applies to CSV product imports, which return a273 `v2.commerce.product_catalog_import` object with a274 `status: "awaiting_upload"` and an `upload_url` — useful275 internally to Stripe, not an artifact your integration276 consumes. Always ask for the individual `price_…` IDs277 when you need them, and verify the `price_…` IDs the278 integration uses by re-fetching the deployed bundle279 (`GET /accounts/<id>/workers/scripts/<name>` from the280 Cloudflare API returns the raw source) before declaring281 the catalog wired.282- **Setting `price: "149"` (no decimal).** The decimal is283 technically required by the schema, even when zero. Always284 `"149.00"`.285- **Forgetting `availability: "https://schema.org/InStock"`.**286 AI models surface availability in recommendations. Omitting it287 leaves the field ambiguous and downranks the listing.288- **Treating AI visibility as one-and-done.** It is not. Models289 re-crawl on a lag of days to weeks. Re-validate monthly. Add290 citations as you earn them. Update the FAQ as buyers ask new291 questions.292293## Deployment discipline294295The build is not the ship. The full ship sequence for any296AI-visibility deliverable:2972981. **Local validation** — `python scripts/validate-json-ld.py path/to/page.html`299 reports 0 issues.3002. **Visual smoke test** — `python3 -m http.server` locally, open the301 page in the browser, confirm the visible HTML matches the302 JSON-LD content (no `<details>` mismatches, no broken303 design-system inheritance).3043. **Git** — `git add`, `git commit -m "<message that mentions AI305 visibility>"`, `git push origin <branch>`. Confirm the push306 landed: `git log --oneline origin/<branch> -1`.3074. **Wait for deploy** — GitHub Pages typically redeploys in308 30–90 seconds. Poll with `curl -I <live-url>` until the309 `Last-Modified` or `ETag` advances.3105. **Verify the live HTML** — `curl -s <live-url> | grep -c311 'application/ld+json'` should match the count of JSON-LD312 blocks you wrote. `curl -s <live-url> | grep -oE '"@type":313 "[^"]+"' | sort -u` should list every type you wrote.3146. **Only then** say "live." Anything before step 6 is "built and315 ready to ship" — not the same claim.316317If the user has to come back and ask "is it actually live?," you318broke this discipline. Apologize briefly, finish the ship, and319re-check your mental model of what "done" means in this skill.320321## Linked files322323- `references/json-ld-snippets.md` — copy-paste-ready JSON-LD324 blocks for the most common small-business shapes (solo founder325 service, multi-product shop, local business, B2B SaaS).326- `references/policy-page-templates.md` — the three pages327 (Terms of Service, Privacy Policy, Refund Policy) that Stripe,328 Google Play, Apple App Store, and most payment processors329 require before they will let a small business charge cards. AI330 crawlers also use them as authoritative corpus. Includes the331 "short version" callout pattern and the "not legal advice"332 footer language.333- `templates/faq-page.html` — full `/faq/index.html` skeleton334 matching the Offline Helper warm-paper design, ready to adapt335 to any brand by swapping the `:root` custom properties and336 the `mainEntity` Q&A list.337- `scripts/validate-json-ld.py` — the re-parse loop, plus a338 required-field check, runnable as a single command.339340## Policy pages — a hidden AI-visibility + payment-prerequisite gap341342The same Stripe / HubSpot / Ahrefs audits that flag "AI says343nothing about you" almost always also flag (or worse, *don't*344flag and then block go-live) a separate gap: **no Terms, no345Privacy, no Refund policy at real URLs on the business's own346domain.** Two consequences:3473481. **Stripe will block go-live.** The Live-mode onboarding form349 has a "Business settings" step with three URL fields that350 default to `https://example.com/...`. Saving those placeholders351 is technically allowed but produces a real legal exposure:352 every receipt and Checkout page will link to Stripe's generic353 docs at `example.com/terms` and a privacy policy that does not354 exist. Multiple US states (CA, VA, CO, CT, UT, …) require a355 real, accessible privacy policy before a business can collect356 payment data. Fix: ship the three pages on the business357 domain *before* pasting the URLs into Stripe.3582. **AI crawlers use the policies as authoritative corpus.** When359 ChatGPT or Perplexity is asked "is this business legit / what360 is their refund policy," the model often quotes the business's361 own policy page verbatim. Missing policies are a real362 citation loss, not just a legal risk.363364The three pages take ~90 minutes to write. Each is plain English,365US-law-aware, and matches the rest of the site's design system.366See `references/policy-page-templates.md` for the templates and367the "short version / not legal advice" callout pattern that368lands well with both regulators and AI models.369370## When you only have 90 minutes — the quick-build order371372The full playbook above is comprehensive. When the user is in373a hurry (a launch tomorrow, a press cycle, a Stripe audit they374just discovered) and only has ~90 minutes, ship **only the two375code-only gaps** in this order and defer the rest:3763771. **Identify 5–7 ready-to-buy buyer prompts from the audit.**378 Group by intent (research vs comparison vs ready-to-buy) and379 prioritize the ready-to-buy ones first — those are the380 prompts where a citation directly converts.3812. **Write the answers in human prose first.** 50–150 words382 each, plain-spoken, mentioning the product by name, linking383 to the relevant product/fit-check page. AI models quote these384 answers verbatim; bad prose = bad citations.3853. **Wrap them in `FAQPage` JSON-LD.** New `/faq/` page. Test386 that every block parses with `json.loads` (no trailing387 commas, no single quotes) before commit.3884. **Build the `Organization` + `Service` blocks for the389 homepage.** One `<script type="application/ld+json">` block,390 multiple entities combined via `@graph`.3915. **Validate, ship, verify live.** Schema Markup Validator392 + Google Rich Results Test → `git add`/`commit`/`push` →393 `curl -I` the live URL → grep the served HTML for394 `application/ld+json` → only then report "live."395396Gaps 3 (citations) and 4 (reputation) take months of work and397are not in scope for the quick build. The user will need a398follow-up to chase Skool/podcast/Reddit mentions and to start399collecting real reviews. The `ai-visibility-build` skill was400the previous quick-build-only skill; it has been consolidated401into this one — the full playbook above plus the prioritized402quick-build order here is the new canonical home.403404## Schema.org type selection — quick reference405406| Site shape | Types to emit |407|---|---|408| Single-product / single-service | `Organization` + `Service` (with `Offer`) + `FAQPage` |409| Multi-product catalog with prices | `Organization` + 1× `Service` per product (each with its own `Offer`) + `FAQPage` |410| E-commerce with multiple SKUs | `Organization` + multiple `Product` (each with `Offer`) — use `Product`, not `Service`, when the customer is buying a thing, not a session |411| Subscription product | `Service` + `Offer` with `priceSpecification: { @type: UnitPriceSpecification, unitText: "MONTH" }` |412| Free trial + paid | Two `Offer` blocks (one with `price: "0"`, one with the paid price) OR a single `Offer` with `priceSpecification` and a `subscription_data.trial_period_days` reference in the description |