/ โ† AI Agent Compatibility Fixes / Structured Data for Agents
In this guide
  1. Why agents lean on structured data
  2. Match each customer question to a property
  3. How to see what an agent actually receives
  4. The fix, step by step
  5. Two examples you can adapt
  6. When markup and page disagree
  7. Where the markup comes from on common platforms
  8. Actions, llms.txt and other things people ask about
  9. Keeping it right after launch
  10. Questions and answers

Why agents lean on structured data

An agent working for a customer is not browsing for pleasure. It has a task, such as "find a pair of size 9 walking boots under £120 that can arrive by Friday", and it needs to decide quickly whether your page can help. It can try to read the answer from the visible page, but prices appear in several places (was, now, per unit, with or without delivery), stock messages are phrased differently on every site, and opening hours are often a picture or a free-text paragraph. Each guess is a chance to get it wrong.

Structured data in JSON-LD removes the guesswork. A property called price with a value of 115.00 and a priceCurrency of GBP means one thing only. The agent can read it in milliseconds, and so can the lighter tools that fetch the HTML without running scripts, which cannot see anything that only appears after JavaScript runs.

Two points to keep in proportion:

If you are also working on search appearance, the Product schema errors guide covers Google's required fields for product snippets and merchant listings. This guide takes the agent's point of view: which facts an agent needs and how to make sure it can find them.

Match each customer question to a property

Start from the questions a person would ask before handing over money or turning up at your door. Each has a home in schema.org.

The questionWhere the answer livesCommon gap
Can I buy it now?Offer.availability with a schema.org value such as InStock, OutOfStock, PreOrder or BackOrderAlways InStock because the theme hard-codes it
How much, in what currency?Offer.price and Offer.priceCurrencyPrice written as "£19.99" in a text field, or the pre-sale price left in the markup
Which size or colour?ProductGroup with hasVariant and variesByOne Product for the whole range with a single price
What does delivery cost and how long does it take?Offer.shippingDetails with shippingRate and deliveryTimeMissing altogether, so the agent cannot compare total cost
Can I send it back?hasMerchantReturnPolicy with the return window and feesPolicy only on a separate page in prose
Are you open on Sunday, or on a bank holiday?openingHoursSpecification and specialOpeningHoursSpecificationHours only in the footer as text, holidays not mentioned anywhere
Where are you and how do I call?address, geo, telephone on a LocalBusiness subtypePhone number only as an image or a click-to-reveal button
When is it, is it still on, where do I get tickets?Event.startDate, eventStatus, location, offers.urlDates without a time zone, cancelled events still marked as scheduled
Do you cover my area and what does it cost?Service with areaServed, provider and an offers price or price rangeServices described only in marketing copy

You do not need every row. A plumber needs the LocalBusiness and Service rows; an online shop needs the Offer rows; a theatre needs the Event rows. Pick the ones that match what people come to your site to do.

How to see what an agent actually receives

The AI Agent Compatibility audit reports missing structured data as one of its findings. To see the detail for yourself, check three things.

1. Is the markup in the first HTML?

Open the page, then view the source (Ctrl+U or Cmd+Option+U), not the Elements panel in developer tools. The Elements panel shows the page after scripts have run, so markup added by Google Tag Manager or a client-side app appears there even though a fetcher never sees it. From a terminal, the same check is:

curl -s https://www.example.co.uk/boots/fell-walker | grep -c 'application/ld+json'

A count of zero means no JSON-LD reaches tools that do not run JavaScript.

2. Is it valid and complete?

Paste the address into the Schema Markup Validator at validator.schema.org, which checks against the full schema.org vocabulary, and into Google's Rich Results Test, which checks Google's own requirements. Neither tool knows what agents need, so use the table above as your checklist for completeness.

3. Does it agree with the page?

Put the markup and the visible page side by side and compare price, stock, hours and dates. Then change something on the page, such as picking a different size, and check whether the markup was built for the default variant only. Disagreements are the most damaging finding of all, because they make the agent doubt everything else.

The fix, step by step

Step 1

Write down the questions each page must answer

List your main page templates, such as product, category, location, service and event pages, and for each write the three or four questions a customer needs answered before acting: price, stock, delivery, returns, hours, dates or coverage. Mark which answers are already visible on the page. This list becomes the specification for your markup, and it stops you adding types that look impressive but answer nothing a person asks.

Step 2

Put the markup in the HTML the server sends

Generate the JSON-LD in the page template on the server so it arrives in the first response, ideally in the head. Move any structured data that is currently injected by Google Tag Manager, a chat widget or a client-side framework into the template. Check with view-source or curl that exactly one block per thing appears, and remove duplicates added by a second plugin, an app or an old hard-coded snippet.

Step 3

Complete every offer

Give each Offer a plain number in price, a three-letter priceCurrency, a full schema.org URL in availability and the page address in url. Add shippingDetails with the delivery cost and transit time to your main destination, and hasMerchantReturnPolicy with the return window and whether returns are free. Only add priceValidUntil when a sale genuinely ends on a known date, and remove it once the sale is over.

Step 4

Group variants instead of repeating products

When one item comes in several sizes or colours with their own price or stock, wrap them in a ProductGroup with a shared productGroupID, list the attributes that change in variesBy, and add each variant as a Product in hasVariant with its own sku, size or color and its own offer. That lets an agent tell the customer that size 9 is in stock even when size 10 is not.

Step 5

Add regular and holiday opening hours

Use the most specific LocalBusiness subtype that fits, such as Bakery or Plumber, with a full address and a telephone in international format. Describe normal hours with openingHoursSpecification, one entry per group of days with the same times. Add specialOpeningHoursSpecification entries with validFrom and validThrough dates for bank holidays and closures, and update them a few weeks before each holiday season so they are never stale.

Step 6

Give events a status, real times and a ticket link

Write startDate and endDate as full ISO 8601 values with the UK offset, such as 2026-11-14T19:30:00+00:00 in winter and +01:00 in summer. Set eventStatus to scheduled, postponed, rescheduled or cancelled as things change, name the venue in location with its address, and point offers.url at the page where tickets can actually be bought, with the ticket price and current availability, including sold out.

Step 7

Check the markup against the visible page

Run each template through the Schema Markup Validator and the Rich Results Test, then compare every price, stock status, date and set of hours in the markup with what the page shows. Test a sale item, an out-of-stock item and a variant other than the default. Run the AI Agent Compatibility audit again, and add a check to your release routine so template changes cannot silently break the markup.

Two examples you can adapt

A product with sizes, delivery and returns

This fictional shop sells one boot in two sizes. Size 9 is in stock and size 10 is on back order, which a single Product with a single offer could not express.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ProductGroup",
  "name": "Fell Walker leather boot",
  "productGroupID": "FW-BOOT",
  "brand": { "@type": "Brand", "name": "Ridgeline Outdoor" },
  "image": "https://www.example.co.uk/img/fell-walker.jpg",
  "variesBy": "https://schema.org/size",
  "hasVariant": [
    {
      "@type": "Product",
      "name": "Fell Walker leather boot, UK 9",
      "sku": "FW-BOOT-09",
      "size": "UK 9",
      "offers": {
        "@type": "Offer",
        "url": "https://www.example.co.uk/boots/fell-walker?size=9",
        "price": "115.00",
        "priceCurrency": "GBP",
        "availability": "https://schema.org/InStock",
        "shippingDetails": {
          "@type": "OfferShippingDetails",
          "shippingRate": { "@type": "MonetaryAmount", "value": "0", "currency": "GBP" },
          "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "GB" },
          "deliveryTime": {
            "@type": "ShippingDeliveryTime",
            "handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" },
            "transitTime": { "@type": "QuantitativeValue", "minValue": 1, "maxValue": 3, "unitCode": "DAY" }
          }
        },
        "hasMerchantReturnPolicy": {
          "@type": "MerchantReturnPolicy",
          "applicableCountry": "GB",
          "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
          "merchantReturnDays": 30,
          "returnMethod": "https://schema.org/ReturnByMail",
          "returnFees": "https://schema.org/FreeReturn"
        }
      }
    },
    {
      "@type": "Product",
      "name": "Fell Walker leather boot, UK 10",
      "sku": "FW-BOOT-10",
      "size": "UK 10",
      "offers": {
        "@type": "Offer",
        "url": "https://www.example.co.uk/boots/fell-walker?size=10",
        "price": "115.00",
        "priceCurrency": "GBP",
        "availability": "https://schema.org/BackOrder"
      }
    }
  ]
}
</script>

Each variant has its own address with a query parameter, so an agent can send the customer straight to the right size. In a real shop, give every variant the same shipping and returns details, or define them once at organisation level where your platform supports it.

A local business with holiday hours

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Bakery",
  "name": "Tollgate Bakehouse",
  "url": "https://www.example.co.uk/",
  "telephone": "+44 1632 960123",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "4 Tollgate Row",
    "addressLocality": "Exampleton",
    "postalCode": "EX1 2AB",
    "addressCountry": "GB"
  },
  "openingHoursSpecification": [
    { "@type": "OpeningHoursSpecification", "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "opens": "07:30", "closes": "16:00" },
    { "@type": "OpeningHoursSpecification", "dayOfWeek": "Saturday", "opens": "08:00", "closes": "14:00" }
  ],
  "specialOpeningHoursSpecification": [
    { "@type": "OpeningHoursSpecification", "validFrom": "2026-12-25", "validThrough": "2026-12-26", "opens": "00:00", "closes": "00:00" }
  ]
}
</script>

Sunday is simply left out, which means closed. The Christmas entry sets opening and closing to the same time, the usual way to say "closed all day" for a date range. The phone number uses a range reserved for fiction, so replace it with your own.

When markup and page disagree

A missing property makes an agent look elsewhere. A wrong one makes it confidently wrong. These are the usual causes.

โš ๏ธ Never mark up something the page does not show, such as a rating that only exists in the JSON-LD or a price for a product that cannot be bought. Search engines treat this as spammy structured data, and an agent that relays it to a customer creates a complaint you then have to handle.

Where the markup comes from on common platforms

Most sites do not write JSON-LD by hand. Knowing which part of your system produces it tells you where to fix it.

Actions, llms.txt and other things people ask about

Schema.org includes potentialAction with types such as BuyAction, OrderAction and ReserveAction, which can describe how to start a task, for example a booking address. They are part of the vocabulary and harmless to add when they are accurate, but the main facts in this guide matter far more, and we are not aware of agents that depend on actions to complete a task. Treat them as optional.

An llms.txt file is a different thing: a plain-text guide to your site's important pages for AI tools. It helps readers of your content, while structured data helps agents check facts on a specific page. They complement each other, and the Agent Readiness Guide covers llms.txt.

FAQPage markup no longer earns rich results for most sites, because Google now limits them to well-known government and health sites. It does no harm where the questions and answers are visible on the page, but it will not tell an agent your price or opening hours, so do not treat it as a substitute.

Keeping it right after launch

Questions and answers

1. Do AI agents actually read JSON-LD structured data?

Many do, particularly tools that fetch a page without running a browser, and agents that do run a browser can read it too because it is part of the HTML. No provider publishes a full list of what it reads, so treat structured data as a clear, reliable second copy of your key facts rather than a guaranteed input. The visible page still matters, and the two must agree, because a mismatch can make an agent stop or report the wrong figure.

2. Is structured data for agents different from structured data for Google?

The vocabulary is the same: schema.org types such as Product, Offer, LocalBusiness and Event in JSON-LD. What differs is which properties matter most. Google publishes required and recommended fields for each rich result, while an agent simply needs the facts to finish a task, such as stock, delivery cost, return window and holiday hours. Sites that did the minimum for rich results often leave those out, which is why this guide focuses on them.

3. My markup is added by Google Tag Manager. Is that a problem?

It can be. Markup injected by Tag Manager only exists after scripts run in a browser. Tools that fetch the raw HTML never see it, and even browser-based agents may read the page before the tag fires. Google can process it after rendering, but that is not the same as every agent seeing it. Move the JSON-LD into your page template so it is part of the HTML your server sends, and remove the tag once the template version is live.

4. How do I show sizes and colours with different stock levels?

Use a ProductGroup for the item as a whole, with a productGroupID and a variesBy property naming the attributes that change, such as size or colour. Then add each variant as its own Product inside hasVariant, with its own sku, the attribute value and its own Offer carrying price and availability. Give each variant an address that selects it, often with a query parameter, so an agent can send the customer straight to the right option.

5. How do I mark up bank holiday opening hours?

Keep your normal week in openingHoursSpecification and add a separate specialOpeningHoursSpecification entry for each holiday or closure, with validFrom and validThrough dates. For a day when you are closed, set opens and closes to the same time, commonly 00:00. For shorter hours, give the actual times. Add the entries a few weeks before the date, check them against the notice in your window, and remove them afterwards, so the markup never promises hours you are not keeping.

6. Should the price in my markup include VAT and delivery?

The price in the markup should be the same amount the page shows the customer for the item, which for UK consumer sites normally includes VAT. Delivery belongs in shippingDetails rather than being added to the price, so an agent can see both the item price and the delivery cost and work out the total. This is not legal advice, so check your own pricing obligations with a qualified adviser if you are unsure.

7. Will adding more schema types make agents prefer my site?

Not on its own. Adding types that do not match what the page offers adds nothing and can look like spam. What helps is complete, accurate markup for the few things that matter to your customers: offers for a shop, hours and contact details for a local business, dates and ticket links for events. An agent that can confirm price, stock and delivery quickly has fewer reasons to move on to another site.

Check what AI agents can read from your page

The AI Agent Compatibility audit tests your page for bot blocks, content hidden behind JavaScript, unlabelled forms and controls, navigation agents cannot follow and missing structured data, and links each finding to the guide that fixes it.

Open AI Agent Compatibility Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All AI Agent Compatibility Fixes  ·  Fixing JavaScript-Only Content  ·  Product Schema Errors  ·  AI Agent Compatibility Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.