The full verification audit of Stackbirds (stackbirds.xyz) across three layers of discovery — classic search (SEO), answer engines (AEO) and generative engines (GEO) like ChatGPT, Perplexity and Gemini. It checks what's actually shipped, scores current visibility and lists the prioritised fixes.
Crawl, meta, headings, schema, Core Web Vitals and indexation — verified against what's live on the site.
Featured snippets, People-Also-Ask and voice answers — whether Stackbirds is the quoted answer, not just a link.
ChatGPT, Perplexity & Gemini — whether Stackbirds is named and cited when someone asks an AI about agentic browsers.
Can't see the report above? Open the PDF directly →
Copy-paste code for the five highest-impact fixes from the report. Targeted at the existing Next.js App Router setup. Replace values in UPPERCASE before shipping.
The live site returns 403 to non-browser user-agents, which can block Googlebot and AI crawlers (GPTBot, PerplexityBot, ClaudeBot). Allow them at the edge before any other fix can take effect.
// middleware.ts (repo root)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const ALLOWED_BOTS = [
"googlebot", "bingbot", "duckduckbot", "applebot",
"gptbot", "oai-searchbot", "chatgpt-user", // OpenAI
"perplexitybot", "perplexity-user", // Perplexity
"claudebot", "claude-web", "anthropic-ai", // Anthropic
"google-extended", "ccbot",
];
export function middleware(req: NextRequest) {
const ua = (req.headers.get("user-agent") || "").toLowerCase();
const isBot = ALLOWED_BOTS.some(b => ua.includes(b));
const res = NextResponse.next();
if (isBot) res.headers.set("x-allow-bot", "1");
return res;
}
export const config = { matcher: "/((?!_next|favicon|api).*)" };
Then: in the WAF / Cloudflare / Vercel Firewall rule currently returning 403, exempt requests whose UA matches the list (or whose response header carries x-allow-bot: 1). Verify with curl -A "GPTBot" https://stackbirds.xyz/ — should return 200.
Add to app/layout.jsx, inside the document so AI engines can confirm the brand entity.
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify({
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://stackbirds.xyz/#org",
"name": "Stackbirds",
"url": "https://stackbirds.xyz/",
"logo": "https://stackbirds.xyz/logo.png",
"description": "Self-trained agents for ops teams — record a workflow once, an agent runs it forever.",
"sameAs": [
"https://twitter.com/STACKBIRDS_HANDLE",
"https://www.linkedin.com/company/STACKBIRDS_HANDLE",
"https://github.com/stackbirds-engineering"
]
},
{
"@type": "SoftwareApplication",
"@id": "https://stackbirds.xyz/#app",
"name": "Stackbirds",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Web",
"url": "https://stackbirds.xyz/",
"description": "Self-trained agents for ops teams.",
"publisher": { "@id": "https://stackbirds.xyz/#org" },
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }
}
]
})}}
/>
Create both files under app/. Next.js serves them at /robots.txt and /sitemap.xml automatically.
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: [{ userAgent: "*", allow: "/", disallow: ["/api/"] }],
sitemap: "https://stackbirds.xyz/sitemap.xml",
host: "https://stackbirds.xyz",
};
}
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
const base = "https://stackbirds.xyz";
const now = new Date();
return [
{ url: `${base}/`, lastModified: now, changeFrequency: "weekly", priority: 1.0 },
{ url: `${base}/support`, lastModified: now, changeFrequency: "monthly", priority: 0.6 },
{ url: `${base}/policy`, lastModified: now, changeFrequency: "yearly", priority: 0.3 },
];
}
Replace the existing metadata export in app/layout.jsx.
// app/layout.jsx
export const metadata = {
metadataBase: new URL("https://stackbirds.xyz"),
title: {
default: "Stackbirds — self-trained agents for ops teams",
template: "%s | Stackbirds",
},
description:
"Record a workflow once. A self-trained agent runs it forever. Built for ops teams.",
alternates: { canonical: "/" },
openGraph: {
type: "website",
siteName: "Stackbirds",
title: "Stackbirds — self-trained agents for ops teams",
description: "Record a workflow once. An agent runs it forever.",
url: "https://stackbirds.xyz/",
images: [{ url: "/og.png", width: 1200, height: 630, alt: "Stackbirds" }],
},
twitter: {
card: "summary_large_image",
title: "Stackbirds — self-trained agents for ops teams",
description: "Record a workflow once. An agent runs it forever.",
images: ["/og.png"],
creator: "@STACKBIRDS_HANDLE",
},
icons: { icon: "/icon.png", apple: "/apple-icon.png" },
};
Then: drop a 1200×630 share image at public/og.png and a 512×512 mark at public/icon.png. Override the inherited description in app/policy/page.jsx and app/support/page.jsx with their own export const metadata.
Adds a real FAQ block to the homepage and marks it up so it can win featured snippets and AI-answer citations.
// components/Faq.jsx
const FAQS = [
{ q: "What is Stackbirds?",
a: "Stackbirds is a platform for self-trained agents for ops teams — you record a workflow once, and an agent runs it forever." },
{ q: "How do self-trained agents work?",
a: "You demonstrate a task in the browser. Stackbirds captures the steps, generalises them, and replays them autonomously — typically training in about five minutes." },
{ q: "Is it secure?",
a: "SOC 2 ready, SSO included, and agents run in isolated browser sessions per workflow." },
{ q: "Who is it for?",
a: "Ops, RevOps and back-office teams that move data between SaaS tools and want to automate without writing scripts." },
];
export default function Faq() {
return (
<section id="faq">
<h2>Frequently asked questions</h2>
{FAQS.map(f => (
<details key={f.q}><summary>{f.q}</summary><p>{f.a}</p></details>
))}
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": FAQS.map(f => ({
"@type": "Question",
"name": f.q,
"acceptedAnswer": { "@type": "Answer", "text": f.a }
}))
})}} />
</section>
);
}
Bonus AEO: add a one-line "Stackbirds is…" definition above the hero so AI engines have a clean extractable claim.
A week-by-week implementation plan to turn stackbirds.xyz from an SEO 5 / GEO 4 / AEO 3 page into one that AI answer engines (ChatGPT Search, Perplexity, Gemini, Google AI Overviews) confidently cite when someone asks about agentic browser automation for ops. Targets shipped fixes, not vanity tickets.
Concrete: crawlers receive 200 OK · brand entity, product and FAQ are in JSON-LD · 4-6 real FAQ answers shipped · OG share + favicon live · combined audit score moves from 12/30 to 22+/30 by 29 Jun. Re-test by asking Perplexity / ChatGPT Search "what is Stackbirds" and checking the citation.
curl -A "GPTBot" → 200.app/layout.jsx.app/icon.png) + apple-touch-icon./policy and /support their own meta descriptions; consider noindex on utility pages.href="#" links (Pricing, Docs, Blog, Terms, Security) — real pages or remove.seo-geo-aeo audit; regenerate the verification PDF as v3; update the Status Log.Inputs and decisions to unblock each week. The plan can ship at this pace only if these land in week 1.
stackbirds-engineering/stackbirds-site for the implementer.sameAs: Twitter, LinkedIn (company), GitHub org.curl -A "GPTBot" + GSC URL inspect/skill seo-geo-aeo re-runDated record of audit revisions and any shipped fixes.