# agentsite — SEO for agents

> agentsite makes client-rendered SPAs readable to AI crawlers without changing the customer's source. We render the page in headless Chromium and serve the result back through a small request-path runtime that the customer installs in front of their app.

**Auth:** every authenticated call uses `Authorization: Bearer <token>`. The user mints a Personal Access Token (`asit_…`) at <https://agentsite.app/settings/tokens>; agents act with that. See `/agent.md` §1 for the full auth flow.

**Deeper reads (fetch these when you need detail — keep this file short):**

- **Agents: start here.** Agent quick-start (auth, smallest useful loop, etiquette): <https://api.agentsite.app/agent.md>
- **OpenAPI** (machine-readable spec; per-operation `security.bearer` lists the PAT scope each endpoint needs): <https://api.agentsite.app/openapi.yaml>
- **Install templates + decision rule:** <https://api.agentsite.app/install.md>
- **Detailed API reference** (auth modes, cache semantics, JSON-LD detection, response headers, examples): <https://api.agentsite.app/docs.md>
- **Reference demos** (byte-for-byte install results): <https://github.com/agentsite/integration>

## Install patterns

Four shipped patterns — pick by stack shape, then fetch `/install.md` for the actual templates:

- **Express** — static SPA + Node container.
- **Express-Sidecar** — non-streaming SSR framework + Node sidecar (Next pages, Nuxt 2, Rails, Django, WordPress, etc.).
- **Edge** — Vercel / Cloudflare Pages / Netlify edge runtime.
- **Nginx** — nginx-fronted static SPA, zero code in the customer's deploy.

**On the roadmap, not yet shipped:** SDK, njs, CloudFront, Python, PHP. If the customer's stack matches one of these, offer the closest shipped pattern as a stopgap; don't improvise.

## Endpoints

| Method | Path                  | Returns                                  |
|--------|-----------------------|------------------------------------------|
| GET    | /render?url={url}     | final asset bytes dispatched by URL path — robots / sitemap / llms / well-known / .md / HTML. The **Nginx** install pattern points at this for every path class. `Accept: application/json` returns the legacy RenderBundle JSON (back-compat for deployed snippets that pre-date /bundle). |
| GET    | /bundle?url={url}     | application/json — RenderBundle. Explicit-name endpoint for the JSON contract. The current snippets (`@agentsite/express`, `@agentsite/edge`) call this. |
| GET    | /render.md?url={url}  | text/markdown — YAML frontmatter + body  |
| GET    | /llms-index?site={host} | application/json — { llmsTxt, llmsFullTxt, entryCount }. Kept for snippet back-compat; the **Nginx** pattern uses /render?url=…/llms.txt instead. |
| GET    | /robots-policy?site={host} | text/plain — granular AI-bot robots.txt for the site. Kept for snippet back-compat; the **Nginx** pattern uses /render?url=…/robots.txt. |
| GET    | /snippet.js           | drop-in Express handler for SPA fallback |

## Auth

**`Authorization: Bearer <token>` required on every call.** Two shapes accepted: a JWT session cookie (dashboard) or a PAT (`asit_…`, agents + snippet runtime). PATs carry explicit scopes — see `/agent.md` §1 or the `security` block in `/openapi.yaml`. Token-less requests get 401.

## Cache

Send `Cache-Control: no-cache` to force a fresh render.

Response headers:

- `X-Cache: HIT | MISS | BYPASS`
- `X-Render-Status: ready | generating` (a `generating` placeholder is returned while the real render runs in the background; retry shortly)
- `X-Render-Engine`, `Last-Modified`, `Expires` (suppressed on placeholders)

Detailed cache semantics, error codes, JSON-LD detection rules, body-injection modes, and the full /env.js / /robots.txt / /llms.txt behavior are documented in </docs.md> — fetch it when you need to act on those details.
