Oxwyn Studio
All field reports
23 July 2026AEO

JSON-LD that AI engines can actually use

Which schema.org types earn their keep for AI answers, the XSS trap nobody mentions, and the structured-data lies that get sites ignored.

For a decade, JSON-LD had one job: win rich results in Google. Stars, FAQs, breadcrumbs, the occasional recipe card. That era is mostly over, and something more interesting has replaced it. Structured data is now the cheapest way to tell an AI answer engine, in a format it cannot misread, exactly what your business is, what you sell and what your pages claim.

The catch is that most structured data on the web is either decorative, broken or dishonest. This is what we actually ship on oxwynstudio.com, why, and the two traps (one security, one credibility) worth knowing about before you copy a snippet off a blog.

What structured data does for AI engines, versus rich results

JSON-LD (JSON for Linked Data, a block of machine-readable JSON in a script tag describing the page) was designed for search engines that wanted facts without parsing prose. Classic Google used it to decorate listings: mark up FAQs, maybe get an expandable panel under your result. That game has shrunk. Google restricted FAQ rich results to a handful of authoritative site types back in 2023, and most visual rewards have narrowed since.

AI answer engines use the same markup differently. A language model reading your page is doing interpretation, and interpretation has error bars. Prose that says "we build subscription websites from £99 a month" can be misread, mis-attributed or blended with a competitor's claims. A JSON-LD Organization node stating your name, URL, logo and services is a set of typed facts with no ambiguity to resolve. You are not decorating a listing any more; you are disambiguating an entity, and reducing the chance an engine describes you wrongly when someone asks who you are.

That reframing changes what "worth marking up" means. The question is no longer "does Google show a widget for this type". It is "would I want an AI system to have this fact as data rather than inference".

A business being found on a phone: the retrieval surface where structured facts decide how you are represented

The types that earn their keep on a business site

Schema.org defines hundreds of types. On a business site, four do nearly all the work, and they happen to be the four we ship:

  • Organization: who you are. Name, legal name, URL, logo, contact points, service areas, same-as links to real profiles. This is the anchor node everything else references, and the single most valuable block for entity disambiguation.
  • Article: for anything editorial, like this page. Headline, author, publisher, dates published and modified. Truthful dates matter: engines notice when every article claims to have been modified this morning.
  • FAQPage: question-and-answer pairs, stated plainly. Google barely rewards it visually any more, but the format maps almost perfectly onto how answer engines retrieve: a user asks a question, your markup contains that question with an attached answer. It is retrieval bait in the honest sense.
  • Service: what you actually sell, with names, descriptions and provider links back to the Organization node. For a studio like ours, one per offering, from secure web development to Website-as-a-Service.

Everything beyond these should justify itself. LocalBusiness if you genuinely serve a locality. BreadcrumbList if your hierarchy is deep. Product with offers if you sell discrete products at listed prices. If you cannot say what consumer a type serves, it is dead weight in your HTML.

A FAQPage graph, as actually shipped

Here is the shape of the FAQPage graph on our services pages, trimmed to two questions. Note the @graph array holding multiple nodes in one script tag, and stable @id values so nodes can reference each other:

const jsonLd = {
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://oxwynstudio.com/#organization",
      name: "Oxwyn Studio",
      url: "https://oxwynstudio.com",
      logo: "https://oxwynstudio.com/logo.png",
    },
    {
      "@type": "FAQPage",
      "@id": "https://oxwynstudio.com/website-as-a-service/#faq",
      mainEntity: [
        {
          "@type": "Question",
          name: "What does Website-as-a-Service include?",
          acceptedAnswer: {
            "@type": "Answer",
            text: "A hand-coded website with hosting, maintenance, security updates and ongoing evolution bundled into one monthly fee, from £99 per month plus a one-off build fee.",
          },
        },
        {
          "@type": "Question",
          name: "Do I own the website?",
          acceptedAnswer: {
            "@type": "Answer",
            text: "Yes. The code, content and domain are yours. The subscription covers the ongoing service, not a licence to your own site.",
          },
        },
      ],
    },
  ],
};

One rule governs the content: every answer in the markup appears, in substance, in the visible page. Markup that says things the page does not is cloaking, and both classic and AI crawlers compare the two.

The XSS trap when your JSON-LD comes from a database

Here is the part most tutorials skip. Static, hand-written JSON-LD is safe. But the moment your structured data is built from database content (article titles from a CMS, answers edited in an admin panel), you have user-influenced strings being serialised into a script tag, and JSON.stringify alone does not save you.

The attack is simple: a string containing </script> closes your JSON-LD block early, and whatever follows is parsed as live HTML in your page. JSON escaping does not touch the < character because it does not need to for JSON validity. It only becomes dangerous in an HTML context, which is exactly where JSON-LD lives.

Our article pages are rendered from a Supabase-backed CMS, so every JSON-LD block that touches database strings goes through one escape:

{/* < escape prevents </script> breakout from DB-driven strings */}
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
  }}
/>

Replacing < with its unicode escape \u003c keeps the JSON semantically identical (parsers decode it back to <) while making it impossible for any string in the payload to terminate the script element. One regex, zero cost, and an entire class of stored XSS (cross-site scripting: injected script running in your visitors' browsers) is closed. If your JSON-LD includes anything a human can edit, ship this pattern. If a writer's article title can execute script in a reader's browser, no rich result is worth it.

Security-first engineering at the studio: structured data is injected with the same care as any other untrusted input

Structured-data lies, and what they cost

The fastest way to make all of this worthless is to lie in it. The classics:

  • Fake reviews and fake aggregateRating: a self-assigned 4.9 from 200 invented reviewers. Google has issued manual actions for this for years, and for AI engines the calculus is worse: an engine that can find no trace of those reviewers anywhere else learns that your markup is unreliable, and unreliable markup is simply discounted. You do not get caught once; you get quietly ignored.
  • FAQ stuffing: keyword-dense pseudo-questions no human asked, with answers that exist only in markup.
  • Fabricated organisation claims: founding dates, headcounts, awards and address data that contradict every other source on the web.

The pattern across all three is the same: structured data is a claims channel, and claims channels live or die on corroboration. AI engines cross-reference. When your JSON-LD, your visible copy, your work and third-party mentions all agree, each source reinforces the others. When they disagree, the markup loses, because it is the easiest thing to fake and the engines know it.

So the honest position, and the one we build to: mark up only what is true, only what is visible, and only what another source could confirm. Structured data will not make an AI engine cite you (nobody can guarantee that, and we would distrust anyone who claims otherwise). What it does is remove every avoidable reason for an engine to misread you, and that is worth having on every page that matters. If you want a second pair of eyes on what your current markup actually claims, that review is part of every engagement we start through /contact.

Key takeaways

  • Structured data's job has shifted from winning rich snippets to giving AI engines typed, unambiguous facts about your entity, and the value survives even as visual rich results shrink.
  • Four types do nearly all the work on a business site: Organization, FAQPage, Article and Service, linked into one graph with stable @id values.
  • Everything in markup must also be in the visible page; divergence reads as cloaking to both classic and AI crawlers.
  • If any JSON-LD string comes from a database, escape < as \u003c after stringifying, or a stored </script> becomes XSS.
  • Fake reviews and invented aggregateRating do not just risk penalties; they teach engines to discount everything else you mark up.

Frequently asked

Does JSON-LD still matter now that Google shows fewer rich results?

Yes, but for a different reason. The visual rewards have shrunk, while the interpretation value has grown: AI answer engines use structured data as typed facts about your business, which reduces misreading when they summarise or cite you. Judge markup by clarity delivered, not widgets won.

Which schema.org types should a small business site start with?

Organization first, since it anchors your identity, then Service for each real offering, Article for editorial content and FAQPage where you genuinely answer common questions. Add further types only when you can name the consumer that benefits.

Is it safe to generate JSON-LD from CMS content?

Only with escaping. JSON.stringify does not escape <, so a title containing a closing script tag can break out of the JSON-LD block and execute as HTML. Replace every < with \u003c after stringifying and the payload stays valid JSON while the breakout becomes impossible.

Will adding structured data get my site cited in AI answers?

Nobody can guarantee citation, and that includes us. Structured data improves how engines interpret and represent you, which is a precondition for citation, not a promise of it. Treat it as removing reasons to be misread rather than buying a placement.

Put this to work

Want a system built like this behind your business?

Brief the studio