Checking it worked
Geoffy does not take your word for it. After you publish, we fetch your live product page and look for our markup in the server-rendered HTML. A product stays pending until we find it, and your dashboard count only ever shows products confirmed on your real site.
If a product stays pending
| Reason | What it means | What to fix |
|---|---|---|
code_not_added | We fetched the page and our markup was not in it | The component is not on the page, or it is rendering on the client |
stale_version | Our markup is there, but it is an older copy | Nothing. Your cache clears itself within the revalidate window — one hour by default |
canonical_broken | The page does not name itself as canonical | See "Before you start" — most often a missing per-page metadata export |
You can check the same thing yourself:
curl -s https://yourdomain.com/en/products/your-handle | grep -c 'data-geoffy'A number greater than zero means it is in the server-rendered HTML, which is what matters. The same command works against your dev server while you are building — swap the host.
And the namespace
Geoffy checks the /apps/geoffy mount the same way — by fetching it, not by
trusting the setting. Check it yourself with:
curl -si https://yourdomain.com/apps/geoffy/sitemap.xml | head -20 A 200 is not the check. That is the one thing worth knowing
here, because it is what makes a broken mount look fine: a catch-all route in your app
answers every path — including this one — with your home page and a perfectly
healthy 200.
So Geoffy checks three things instead, and you can check the same three by eye:
| Check | Why |
|---|---|
| The response is genuinely proxied from Geoffy | Not your home page, not a CDN interstitial. If what comes back looks like your own site, a catch-all route is winning and the namespace route is not being reached |
| It is your site's content |
A GEOFFY_SITE_KEY copied from another project would otherwise confirm
a mount that is serving someone else's catalogue under your domain
|
content-type is application/xml or text/xml | A sitemap served as HTML is one a crawler will not parse |
If the mount is not confirmed, the dashboard says which of these it is:
| Reason | What it means |
|---|---|
not_mounted | The path answered, but not with our bytes — usually a catch-all route serving your home page, or the route file is not deployed yet |
wrong_site |
Our marker came back carrying a different site key. Your
GEOFFY_SITE_KEY belongs to another Geoffy site
|
unreachable | We could not fetch it at all. This is not the same as "not mounted" and is not recorded as a failure — we try again |
While the mount is unconfirmed, your llms.txt simply says less: it omits
the product twins and the Geoffy sitemap rather than pointing a crawler at URLs that
answer with your home page.
What a local run cannot do
Geoffy confirms your integration by fetching your live product page at
its canonical URL — the public one, not your machine. So until the integration is
deployed, products stay pending with code_not_added no matter how correct
everything looks locally.
That is the check working as intended: it reports what your visitors and the crawlers
actually get. Prove it locally with the curl above, ship, and let the
confirmation follow.
Next.js specifics
Section titled “Next.js specifics”code_not_added on a page you are sure is correct is almost always one of two things:
- The component is inside a client component.
<GeoffyProduct>is an async server component. Rendering it below a"use client"boundary means it never runs on the server, and the markup exists only after hydration — which is exactly what a crawler does not see. canonicalUrldoes not match. The component renders nothing on a page that is not the one Geoffy published against, leaving an inertdata-geoffy-skippedmarker instead. If yourcurlfinds zerodata-geoffymatches, look for that marker in view-source: it means the component ran and deliberately declined.