# How we make coreai.no visible to search, answer engines, and AI agents

SEO, AEO, and agent friendliness are baked into the coreAI homepage as three layers of the same response: a central SEO builder produces head tags and JSON-LD, a `.md` surface serves the same content as plain markdown to AI bots, and a dedicated sitemap command keeps both in sync on every reindex. All three layers read from the same markdown documents — nothing is bolt-on.

## What we get from existing packages

Three packages cover the routine work, so we don't have to maintain it ourselves:

- `prezet/prezet` — markdown CMS that indexes a localized content directory into a SQLite file. Every heading, lede, and FAQ entry is one markdown file, and content is read through Prezet's query API.
- `spatie/laravel-markdown-response` — global URL-rewrite middleware that strips `.md` off a URL and negotiates `text/markdown` against the `Accept` header. We get agent-friendly URLs without duplicating routes.
- `spatie/laravel-sitemap` — primitives for building sitemap XML and a sitemap index. We orchestrate what ends up inside.

None of them handles JSON-LD or AEO — that's where our own components take over.

## What we built ourselves

Five building blocks, one job each:

- An SEO builder that every controller uses to produce one `seo` view variable. It sets the page title with the brand suffix, toggles optional schemas (FAQ, breadcrumbs), and computes canonical and hreflang from the list of supported locales.
- Five schema.org builders — Organization, WebSite, FAQPage, BreadcrumbList, and Article — each a pure class with one build method that returns JSON-LD as an associative array. Testable in isolation, identical across pages.
- A central head partial that is the only place in the codebase where `<title>`, `<meta>`, `<link rel="canonical">`, hreflang alternates, OG tags, and `<script type="application/ld+json">` get rendered. Pages cannot write their own head.
- A markdown driver with one assembler per editorial page — serves markdown 1:1 from the sources when an agent asks for it. We never convert the rendered HTML back to markdown; we read the same `.md` files the Blade templates do, so the agent sees exactly what the author wrote.
- `/llms.txt` and `/llms-full.txt` — an index of the marketing pages plus the 20 latest articles, and the entire default-locale corpus assembled into a single document. Both are `noindex`, default-locale, and live outside the marketing route group.

A dedicated sitemap command orchestrates the XML into three files: `sitemap.xml` as the index, `sitemap-pages.xml` with marketing routes × locales, and `sitemap-articles.xml` where a centrally maintained blocklist filters out the section sentinels (`welcome-page`, `solutions-page`, …) so marketing documents don't leak into the article sitemap. After every reindex we override Prezet's sitemap hook and additionally run a cache flush against the markdown surface, so agents never read stale content.

## AEO: three rules we grade every text against

AEO (Answer Engine Optimization) is not a library, it's three editorial rules baked into the copywriting process and documented in an internal style guide:

- **Answer first.** The first sentence of every lede and every FAQ answer states the answer outright. No warm-up, no rhetorical questions, no "let's take a closer look at…".
- **One H1 per page.** The hero owns the only `<h1>`. Every other section title is an `<h2>`, nested ones `<h3>`. Skipping levels is forbidden.
- **No "click here" CTAs.** Link text describes the destination ("See pricing", "Order now"), not the gesture.

The rules are enforced in code review, not by lint — but they are why answer engines like ChatGPT Search and Perplexity actually manage to extract the right sentence from a coreAI page.

## How AI agents see the page

Three triggers swap HTML for markdown on the editorial routes: `.md` suffix in the URL, the `Accept: text/markdown` header, and known bot user-agents like `GPTBot` and `ClaudeBot`. All three lead to the same response — the source markdown the author wrote, not a conversion of the rendered HTML. Transactional routes (`/order`, `/contact`) are kept outside the middleware, so `/order.md` returns regular HTML.

Want to see it for yourself? [Get this article as markdown](https://coreai.no/en/articles/seo-aeo-and-agent-visibility.md) — same URL, just with `.md` on the end. That's exactly the response `GPTBot` and `ClaudeBot` get when they visit `/en/articles/seo-aeo-and-agent-visibility`.

The result is that the same content is published to humans, search engines, and AI agents without duplication: the author writes one markdown file, and three surfaces deliver it to wherever the recipient is looking.