Skip to main content
Route each request to one of two destinations, decided on every request. Humans pass through to the normal page, cached and prerendered, untouched. Agents are rewritten internally to a route handler that fetches the same page, adds the card and builds its headers from scratch. Do not rely on Vary: User-Agent to keep the two apart. Before you start, do steps 1 to 3 of the Quickstart: a site, a fragment its pages resolve to, and a delivery key. The code below already applies the rules every edge keeps, listed in Choose your edge: the agent gate, X-Agent-UA, the page cache contract and the serverless timeout budget.
1

Add the classifier

proxy.ts decides human or agent on every request. This is the Next 16 convention; on earlier versions, the same code exports middleware from middleware.ts. The agent markers are a copy of the list in AI agents: keep them in sync.
proxy.ts
2

Add the agent destination

The route handler at app/agent/[[...path]]/route.ts fetches the human copy of the page, calls doubleshift and composes the result. Replace <your account id> and set CANONICAL_ORIGIN to your public origin. Store the API key in the DS_API_KEY environment variable of your project: it is read server-side only and sent in the X-API-Key header.
app/agent/[[...path]]/route.ts
When the self-fetch returns a 200 HTML page, the agent response carries an x-abc-outcome header: served, no_fill, empty, upstream_ followed by the status, fetch_timeout, fetch_failed or no_main. Use it to tell a no-fill from a failed call. A failed self-fetch (502) and a mirrored non-HTML or non-200 answer carry no x-abc-outcome: no call was made.
3

Mark where the brand line goes

Add <div class="ds-anchor"></div> at the head of the article body in your template, on every page that can get a card: the handler replaces it with the brand line, which the contract requires (Where the card must land). Without the anchor, the card still goes at the head of <main> but the page has no disclosure: the integration is incomplete. A page without <main> is served without a card (no_main).
4

Check the requests that skip your proxy

WAF deny or challenge and Deployment Protection end the request before routing. With Standard Protection on, VERCEL_URL sits behind Vercel Authentication and the self-fetch composes the auth page instead of yours: send the Protection Bypass for Automation secret (x-vercel-protection-bypass) on the self-fetch, or protect previews only. Otherwise self-fetch request.nextUrl.origin, which loses deployment pinning during a rollout. On-demand ISR revalidation does not pass through the proxy: revalidate only the canonical path, never /agent/…. Check the matcher for holes.
5

Deploy and verify

Deploy, then run the human and agent checks of the Quickstart, and the cold and warm cache sequence in Verify and troubleshoot.