Skip to content
Gauge

ax-check.com

Lightsage

lightsage.com

ax-check.com

Checked 11 November 2026

A

100/100

Agents need your login

Docs and pricing are clear, but no setup path was ever tested.

15 of 23 checks passed, all in clarity and pricing. 8 checks on quickstart, install, API and MCP docs went unassessed — no agent reached them.

1 fix to start with

Concrete changes, backed by what we found.

Onboarding

Give agents a clear login handoff and a way to resume setup.

Agents cannot progress through onboarding without human help. Provide an agent-accessible path through authentication and setup.

Recorded coding sessions

Same task, 3 models, no lightsage.com account. Recorded 11 November 2026 on Pi.

3 recorded · 2m 13s

2m 13s · recorded

Session findings

Select a finding to see it in context.

piv0.84.2
modelQwen 3.8 Maxcwd/sandbox/repo
Help me build a simple example using Lightsage. Tell me how pricing works, and briefly tell me whether this product will be easy for you to manage. Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop.
Thinking…

I need to figure out what Lightsage is. Let me look at the sandbox/repo.

cd /sandbox/repo && ls -la && cat README* 2>/dev/null | head -50
total 16
total 16
drwxr-xr-x 3 root root 4096 Sep 18 08:00 .
drwxr-xr-x 7 root root 4096 Nov 11 05:08 ..
drwxr-xr-x 8 root root 4096 Sep 24 20:05 .git
-rw-r--r-- 1 root root  408 Sep 18 08:00 .gitignore
cd /sandbox/repo && git log --oneline -5 2>/dev/null; ls -R | head -50
9ebf5fb Initial workspace
9ebf5fb Initial workspace
.:
Thinking…

Empty repo. "Lightsage" — unknown product. Let me check if it's installable via npm/pip. Let me try a quick search offline.

cd /tmp && timeout 60 npm view lightsage 2>&1 | head -20; echo "---PIP---"; timeout 60 pip index versions lightsage 2>&1 | head -5
npm error code E404
npm error code E404
npm error 404 Not Found - GET https://registry.npmjs.org/lightsage - Not found
npm error 404
npm error 404  'lightsage@*' is not in this registry.
npm error 404
npm error 404 Note that you can also install from a
npm error 404 tarball, folder, http url, or git url.
npm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-11-11T05_08_39_193Z-debug-0.log
---PIP---
ERROR: No matching distribution found for lightsage

[notice] A new release of pip is available: 26.2.1 -> 26.3
[notice] To update, run: /opt/freestyle/python/bin/python3 -m pip install --upgrade pip
cd /tmp && timeout 60 npm search lightsage 2>&1 | head -20; echo "=== curl ==="; timeout 20 curl -sS -o /dev/null -w "%{http_code}\n" https://registry.npmjs.org/lightsage 2>&1
@lightsage/mcp
@lightsage/mcp
Model Context Protocol (MCP) server for the Lightsage Public API.
Version 0.4.24 published 2026-11-09 by GitHub Actions
Maintainers: seanflyyy
https://npm.im/@lightsage/mcp

@lightsage/agent-tracker
Track AI agent visits, and revenue attribution for your site. Works with Next.js, Vercel, Cloudflare, Express, and Netlify DNS proxy setups.
Version 0.1.19 published 2026-10-18 by GitHub Actions
Maintainers: seanflyyy
Keywords: ai analytics agent middleware nextjs vercel netlify cloudflare express lightsage
https://npm.im/@lightsage/agent-tracker

@lightsage/mcp-tracker
Track MCP tool calls from AI agents. Works with @modelcontextprotocol/sdk and custom servers.
Version 0.2.3 published 2026-10-19 by GitHub Actions
Maintainers: seanflyyy
Keywords: mcp model-context-protocol ai analytics agent tool-tracking lightsage
https://npm.im/@lightsage/mcp-tracker

=== curl ===
404
Thinking…

Lightsage is a real product: AI analytics / agent tracking, with a Public API and MCP server. Let me look at the packages. The task: build a simple example using Lightsage via SDK or API (hosted product). Pricing info — check docs.

Let me fetch package info and docs.

cd /tmp && timeout 60 npm view @lightsage/agent-tracker --json 2>&1 | head -60
{
{
  "_id": "@lightsage/agent-tracker@0.1.19",
  "_rev": "9-6ee5d4faec4426a0c2c70930ebd44109",
  "name": "@lightsage/agent-tracker",
  "dist-tags": {
    "latest": "0.1.19"
  },
  "versions": [
    "0.1.10",
    "0.1.12",
    "0.1.13",
    "0.1.14",
    "0.1.15",
    "0.1.16",
    "0.1.17",
    "0.1.18",
    "0.1.19"
  ],
  "time": {
    "created": "2026-08-10T04:30:26.482Z",
    "modified": "2026-10-18T07:10:12.815Z",
    "0.1.10": "2026-08-10T04:30:26.791Z",
    "0.1.12": "2026-08-10T15:15:19.873Z",
    "0.1.13": "2026-08-10T16:51:38.363Z",
    "0.1.14": "2026-08-10T17:19:57.923Z",
    "0.1.15": "2026-08-18T02:14:07.735Z",
    "0.1.16": "2026-08-18T02:17:00.543Z",
    "0.1.17": "2026-08-18T19:04:32.252Z",
    "0.1.18": "2026-10-17T02:21:21.052Z",
    "0.1.19": "2026-10-18T07:10:12.590Z"
  },
  "bugs": {
    "url": "https://github.com/lightsagehq/sapient/issues"
  },
  "license": "MIT",
  "homepage": "https://github.com/lightsagehq/sapient#readme",
  "keywords": [
    "ai",
    "analytics",
    "agent",
    "middleware",
    "nextjs",
    "vercel",
    "netlify",
    "cloudflare",
    "express",
    "lightsage"
  ],
  "repository": {
    "url": "git+https://github.com/lightsagehq/sapient.git",
    "type": "git",
    "directory": "packages/agent-tracker"
  },
  "description": "Track AI agent visits, and revenue attribution for your site. Works with Next.js, Vercel, Cloudflare, Express, and Netlify DNS proxy setups.",
  "maintainers": [
    "seanflyyy <seanerwenhan@gmail.com>"
  ],
  "readmeFilename": "README.md",
  "dist": {
    "shasum": "4abe6169685a20e65344a302bf6709e638a6f812",
timeout 30 curl -sSL https://lightsage.com 2>&1 | head -c 3000; echo; echo "=== docs ==="; timeout 30 curl -sSL -o /dev/null -w "%{http_code} %{url_effective}\n" https://docs.lightsage.com 2>&1
Failed<!DOCTYPE html><html data-dpl-id="dpl_GANuutEVbH4wPzxTEXhTQZwtdHcF" lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, in
<!DOCTYPE html><html data-dpl-id="dpl_GANuutEVbH4wPzxTEXhTQZwtdHcF" lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/immutable/chunks/18icq9mamsrgu.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/immutable/chunks/3bh54tvms6ag4.js"/><script src="/_next/static/immutable/chunks/01hgrjxffna3b.js" async=""></script><script src="/_next/static/immutable/chunks/44b6i8chqw4y5.js" async=""></script><script src="/_next/static/immutable/chunks/2jqd0dpb7brv_.js" async=""></script><script src="/_next/static/immutable/chunks/2op6nbgnjz_ep.js" async=""></script><script src="/_next/static/immutable/chunks/2mr2wnm8_dzk2.js" async=""></script><script src="/_next/static/immutable/chunks/turbopack-1byz1flvc-bu1.js" async=""></script><script src="/_next/static/immutable/chunks/08cw52z8ckk5e.js" async=""></script><script src="/_next/static/immutable/chunks/3v5yv7ly4ndil.js" async=""></script><script src="/_next/static/immutable/chunks/0-rrpj5mhacpz.js" async=""></script><script src="/_next/static/immutable/chunks/0v7bysoulql25.js" async=""></script><script src="/_next/static/immutable/chunks/1vo1tbpw_-7c8.js" async=""></script><script src="/_next/static/immutable/chunks/3unacv789iuao.js" async=""></script><script src="/_next/static/immutable/chunks/3xrge241l34qf.js" async=""></script><script src="/_next/static/immutable/chunks/2ui1nxixn-mdh.js" async=""></script><script src="/_next/static/immutable/chunks/0waes8gevbhqn.js" async=""></script><script src="/_next/static/immutable/chunks/35jnkq1_rh3ek.js" async=""></script><script src="/_next/static/immutable/chunks/0v8c1pf0vp7zq.js" async=""></script><script src="/_next/static/immutable/chunks/3vx8ov472_8k6.js" async=""></script><script src="/_next/static/immutable/chunks/2hjkzkc2wrci6.js" async=""></script><script src="/_next/static/immutable/chunks/3b5owu246znbw.js" async=""></script><script src="/_next/static/immutable/chunks/39o_d9v59_kae.js" async=""></script><link rel="preload" href="https://www.googletagmanager.com/gtag/js?id=G-ZJ231P766L" as="script"/><title>Lightsage - Agent-Led Growth Platform for Software Companies</title><meta name="description" content="Lightsage is the Agent-Led Growth Platform for software companies. Win discovery, recommendations, and adoption across AI agents."/><meta name="application-name" content="Lightsage"/><link rel="author" href="https://lightsage.com"/><meta name="author" content="Lightsage"/><meta name="keywords" content="Lightsage,Agent-Led Growth,ALG,Agent Experience,AX,GEO,Generative Engine Optimization,AI visibility,AI coding agents,agent search,agent search traffic,software companies"/><meta name="creator" content="Lightsage"/><meta name="publisher" content="Lightsage"/><meta name="robots" content="index, follow"/><meta name="googlebot" content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"/
=== docs ===
curl: (6) Could not resolve host: docs.lightsage.com
000 https://docs.lightsage.com/


Command exited with code 6
Thinking…

Let me look for docs. Check the npm README of @lightsage/agent-tracker and the MCP package, and look for pricing page on lightsage.com/pricing.

cd /tmp && timeout 60 npm pack @lightsage/agent-tracker --silent 2>&1 | tail -1 && mkdir -p at && tar xzf lightsage-agent-tracker-*.tgz -C at && ls -R at | head -40
lightsage-agent-tracker-0.1.19.tgz
lightsage-agent-tracker-0.1.19.tgz
at:
package

at/package:
README.md
dist
examples
package.json

at/package/dist:
attribution.d.mts
attribution.d.ts
attribution.js
attribution.mjs
browser.d.mts
browser.d.ts
browser.js
browser.mjs
chunk-VV3PAAWX.mjs
chunk-YMLOE6CX.mjs
cloudflare.d.mts
cloudflare.d.ts
cloudflare.js
cloudflare.mjs
express.d.mts
express.d.ts
express.js
express.mjs
index.d.mts
index.d.ts
index.js
index.mjs
netlify.d.mts
netlify.d.ts
netlify.js
netlify.mjs
next.d.mts
next.d.ts
next.js
next.mjs
timeout 30 curl -sSL https://lightsage.com/pricing 2>&1 | python3 -c "import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); print(html.unescape(re.sub(r'\s+',' ',t))[:4000])"
Flexible Pricing - Agent Experience Plans We raised $4M to scale our Agent-Led Growth Platform : We raised $4M to scale our Agent-Led Growth Platform : Read mo
 Flexible Pricing - Agent Experience Plans We raised $4M to scale our Agent-Led Growth Platform : We raised $4M to scale our Agent-Led Growth Platform : Read more → Products Solutions Devtool Arena Resources Enterprise Pricing Log in Start free Start free Products Solutions Devtool Arena Resources Enterprise Pricing Start free Log in Flexible pricing Start with a focused program, then scale as your agent footprint grows. Choose a plan Free For teams getting a first read on their AI visibility. $0 /month Start free No card required 10 prompts tracked weekly on ChatGPT Top tasks from your trial API, MCP, and CLI access Scale Recommended Every coding agent and answer engine, sized by credits. $1,000 /month 19,000 monthly credits Start 7-day trial 7-day trial, no card required All coding agents All answer engines Daily / alternate / weekly schedules API, MCP, and CLI access Usage-based credits for agent runs Enterprise For organizations requiring custom scale and governance. Custom annual Contact sales Custom credits and unit economics All coding agents All answer engines Custom schedules API, MCP, and CLI access Custom content and integrations Custom overages and add-ons Trusted by teams at Full comparison Compare plans What changes from one plan to the next. Free Scale Enterprise $1,000 /month 19,000 monthly credits Start 7-day trial Plans and usage Credits None included 19,000 $0.0526 per credit Custom Estimated monthly capacity 10 prompts weekly ChatGPT only ~1,055 prompts tracked daily on 3 answer engines or ~158 prompts tracked daily on 1 coding agent or ~1,900 agent evals a month Custom Coding agents — Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code Answer engines ChatGPT ChatGPT Google AI Overviews Perplexity Google AI Mode Gemini Search Claude Microsoft Copilot ChatGPT Google AI Overviews Perplexity Google AI Mode Gemini Search Claude Microsoft Copilot Schedules Weekly Up to 10 prompts Daily / alternate / weekly Custom schedules API / MCP / CLI access Included Included Included Integrations — Google Analytics Slack Custom CMS publishing — WordPress Webflow Contentful Sanity Framer GitHub WordPress Webflow Contentful Sanity Framer GitHub Billing and services Credit top-ups — • $100 for 1,600 credits • $200 for 3,200 credits • Never expire • $100 for 1,600 credits • $200 for 3,200 credits • Never expire Trial — • 7 days, no card • 1,000 credits • Unrestricted access Custom Features Agent Discoverability ✓ ChatGPT, weekly ✓ 20 competitors ✓ Unlimited Agent Usability — ✓ ✓ Agent Analytics — ✓ 180 days · Conversion tracking ✓ Unlimited · Conversion tracking Agent Readiness — ✓ Up to 2,500 pages ✓ Unlimited pages Opportunities & Actions ✓ Top tasks from your trial ✓ ✓ Content generation — ✓ Up to 3 per week ✓ Custom Workflows (Premium) — ✓ ✓ MCP Analytics (Premium) — ✓ ✓ Agent Skills Evals (Premium) — ✓ ✓ Automated Reports — Weekly + exports Custom Support Email Email + Slack Custom Managed Services (optional) — +$2,000/month Custom Feature Free $0 /month Start free Scale Recommended $1,000 /month 19,000 monthly credits Start 7-day trial Enterprise Custom annual Contact sales Plans and usage Monthly price $0 $1,000/month Custom Annual agreement Credits None included 19,000 $0.0526 per credit Custom Estimated monthly capacity 10 prompts weekly ChatGPT only ~1,055 prompts tracked daily on 3 answer engines or ~158 prompts tracked daily on 1 coding agent or ~1,900 agent evals a month Custom Coding agents — Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code Answer engines ChatGPT ChatGPT Google AI Overviews Perplexity Google AI Mode Gemini Search Claude Microsoft Copilot ChatGPT Google AI Overviews Perplexity 
Thinking…

Good, pricing is clear. Now let's read the agent-tracker README and examples, and check whether the API requires an API key (which I don't have). Also check @lightsage/mcp and CLI. The key question: can I actually build a working example without credentials? The agent-tracker SDK probably needs a project/site token. Let me read README.

cd /tmp/at/package && wc -l README.md && ls examples && cat package.json
512 README.md
512 README.md
netlify-dns-proxy
{
  "name": "@lightsage/agent-tracker",
  "version": "0.1.19",
  "description": "Track AI agent visits, and revenue attribution for your site. Works with Next.js, Vercel, Cloudflare, Express, and Netlify DNS proxy setups.",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    },
    "./next": {
      "types": "./dist/next.d.ts",
      "import": "./dist/next.mjs",
      "require": "./dist/next.js"
    },
    "./vercel": {
      "types": "./dist/vercel.d.ts",
      "import": "./dist/vercel.mjs",
      "require": "./dist/vercel.js"
    },
    "./cloudflare": {
      "types": "./dist/cloudflare.d.ts",
      "import": "./dist/cloudflare.mjs",
      "require": "./dist/cloudflare.js"
    },
    "./netlify": {
      "types": "./dist/netlify.d.ts",
      "import": "./dist/netlify.mjs",
      "require": "./dist/netlify.js"
    },
    "./express": {
      "types": "./dist/express.d.ts",
      "import": "./dist/express.mjs",
      "require": "./dist/express.js"
    },
    "./browser": {
      "types": "./dist/browser.d.ts",
      "import": "./dist/browser.mjs",
      "require": "./dist/browser.js"
    },
    "./attribution": {
      "types": "./dist/attribution.d.ts",
      "import": "./dist/attribution.mjs",
      "require": "./dist/attribution.js"
    }
  },
  "files": [
    "dist",
    "examples"
  ],
  "scripts": {
    "build": "tsup src/index.ts src/next.ts src/vercel.ts src/cloudflare.ts src/netlify.ts src/express.ts src/browser.ts src/attribution.ts --format cjs,esm --dts --clean --external next --external react",
    "dev": "tsup src/index.ts src/next.ts src/vercel.ts src/cloudflare.ts src/netlify.ts src/express.ts src/browser.ts src/attribution.ts --format cjs,esm --dts --watch --external next --external react",
    "test": "npm run build && node --test tests/*.test.mjs",
    "prepack": "npm run build"
  },
  "publishConfig": {
    "access": "public"
  },
  "keywords": [
    "ai",
    "analytics",
    "agent",
    "middleware",
    "nextjs",
    "vercel",
    "netlify",
    "cloudflare",
    "express",
    "lightsage"
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/lightsagehq/sapient.git",
    "directory": "packages/agent-tracker"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "next": "~16.3.8",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  },
  "overrides": {
    "esbuild": "^0.28.1",
    "postcss": "^8.5.23",
    "nanoid": "^5.1.6",
    "sharp": "^0.35.5",
    "source-map-js": "^1.2.2"
  }
}
read /tmp/at/package/README.md{"content":[{"text":"# @lightsage/agent-tracker\n\nTrack AI coding agents visiting your documentation. Works with Next.js, Vercel, Cloudflare, Express, and Netl…ctrl+o
{"content":[{"text":"# @lightsage/agent-tracker\n\nTrack AI coding agents visiting your documentation. Works with Next.js, Vercel, Cloudflare, Express, and Netlify DNS proxy setups.\n\nNew Agent Analytics keys start with `ls_track_`. Existing Unkey-issued\n`sap_` keys remain accepted during the compatibility migration; no SDK update\nis required when an existing key continues to be used.\n\n## Installation\n\n```bash\nnpm install @lightsage/agent-tracker\n```\n\n## Quick Start\n\n### Browser Visitor Identity\n\nAdd the browser helper once in your client app to identify repeat human visitors\nwith Lightsage's first-party browser fingerprint. It writes visitor/session and\nfirst-touch attribution cookies that the server middleware below automatically\nattaches to tracking events.\n\n```typescript\nimport {\n  identifyUser,\n  initVisitorIdentity,\n  trackConversion,\n} from \"@lightsage/agent-tracker/browser\";\n\nvoid initVisitorIdentity();\n\n// After login/signup, attach your own application user ID.\nvoid identifyUser(\"user_123\");\n\n// Track explicit outcomes when they happen.\nvoid trackConversion(\"signup.completed\", { plan: \"pro\" }, {\n  apiKey: \"ls_track_...\",\n  label: \"Complete sign up\",\n  eventStage: \"completed\",\n});\n```\n\nThe helper has no third-party dependency. It falls back to storage/random IDs\nwhen browser fingerprinting is unavailable. The `userId` value is your customer\napplication's user ID, not a Lightsage account ID.\n\n### Conversion Tracking\n\nUse conversions for outcomes you care about: sign-up intent, sign-up complete,\ndemo requested, checkout complete, API key created. Lightsage does not need every\nclick on the site to close the attribution loop.\n\nProgrammatic conversion:\n\n```typescript\nimport { trackConversion } from \"@lightsage/agent-tracker/browser\";\n\nawait trackConversion(\n  \"signup.completed\",\n  { plan: \"pro\" },\n  {\n    apiKey: \"ls_track_...\",\n    label: \"Sign up\",\n    eventStage: \"completed\",\n  },\n);\n```\n\n`label` and `eventStage` are required. The label is stored as first-class\nconversion event data while `eventName` remains the stable aggregation key.\n\nDeclarative pixel for CTA clicks:\n\n```html\n<script\n  type=\"module\"\n  src=\"https://cdn.example.com/lightsage-browser.mjs\"\n  data-lightsage-key=\"ls_track_...\"\n></script>\n\n<button\n  data-lightsage-conversion=\"signup.click\"\n  data-lightsage-conversion-stage=\"intent\"\n  data-lightsage-label=\"Sign in\"\n>\n  Sign in\n</button>\n```\n\nFor first-party deployments, point `conversionEndpoint` or\n`data-lightsage-endpoint` at your own proxy and add the Lightsage API key on the\nserver instead of exposing it in the browser.\n\n### Visitor Journey Page Views\n\n`initLightsagePixel` records page views for visitors attributed to an AI agent,\non page load and on client-side navigation, so each conversion shows the pages\nthat led to it. Visitors without agent attribution send nothing, and these page\nviews never change your visit counts. A visitor counts as agent-attributed when\ntheir first, session, or current touch came from an agent.\n\nAutomatic capture runs when an `apiKey` or `pageViewEndpoint` is configured. Set\n`autoCapturePageViews: false` to turn it off, and call `trackPageView` yourself\nfor custom routers:\n\n```typescript\nimport { trackPageView } from \"@lightsage/agent-tracker/browser\";\n\nawait trackPageView({ apiKey: \"ls_track_...\" });\n```\n\nFor first-party deployments, point `pageViewEndpoint` at your own proxy to\n`/api/v1/agent-tracking/page-view`.\n\nThe `data-sapient-*` attributes remain deprecated compatibility aliases for\nexisting deployments. New markup should use `data-lightsage-*`.\n\n### Next.js\n\n```typescript\n// middleware.ts\nimport { withAgentTracking } from \"@lightsage/agent-tracker/next\";\n\nexport default withAgentTracking({ apiKey: \"ls_track_...\" });\n\nexport const config = {\n  matcher: [\"/((?!api|_next|admin).*)\"],\n};\n```\n\n### Vercel (Remix, SvelteKit, Astro, Nuxt)\n\n```typescript\n// middleware.ts\nimport { withAgentTracking } from \"@lightsage/agent-tracker/vercel\";\n\nexport default withAgentTracking({ apiKey: \"ls_track_...\" });\n\nexport const config = {\n  matcher: \"/((?!_next|api|favicon.ico|assets|.*\\\\..*).*)\",\n};\n```\n\n### Cloudflare Pages\n\n```typescript\n// functions/_middleware.ts\nimport { onRequest as withTracking } from \"@lightsage/agent-tracker/cloudflare\";\n\nexport const onRequest = withTracking({ apiKey: \"ls_track_...\" });\n```\n\n### Cloudflare Workers\n\n```typescript\nimport { withAgentTracking } from \"@lightsage/agent-tracker/cloudflare\";\n\nconst handler = {\n  async fetch(request, env, ctx) {\n    return new Response(\"Hello!\");\n  },\n};\n\nexport default {\n  fetch: withAgentTracking({ apiKey: \"ls_track_...\" }, handler.fetch),\n};\n```\n\n### Netlify DNS + Framer/Webflow Proxy\n\n```typescript\n// For Framer/Webflow sites whose custom domain is managed in Netlify DNS:\n// netlify/edge-functions/sapient-proxy.ts\nimport { proxyWithAgentTracking } from \"@lightsage/agent-tracker/netlify\";\n\nexport default proxyWithAgentTracking();\n\nexport const config = {\n  path: \"/*\",\n};\n```\n\nSet these environment variables on the Netlify proxy site:\n\n```bash\nLIGHTSAGE_API_KEY=ls_track_...\nORIGIN_URL=https://your-site.framer.website\nSITE_PROVIDER=framer\nSAPIENT_TRACKING_SOURCE=netlify_edge_proxy\n```\n\nNetlify DNS-only does not run tracking code. This setup works by pointing the\ncustom domain at a small Netlify proxy site, then forwarding requests to the\nFramer/Webflow origin.\n\nSee `examples/netlify-dns-proxy` for a minimal Netlify proxy site starter.\nDo not set `ORIGIN_URL` to the custom domain; the proxy rejects that loop.\n\n### Express\n\n```typescript\nimport express from \"express\";\nimport { withAgentTracking } from \"@lightsage/agent-tracker/express\";\n\nconst app = express();\napp.use(withAgentTracking({ apiKey: \"ls_track_...\" }));\n```\n\n## Configuration\n\n```typescript\ninterface LightsageConfig {\n  /** Your Lightsage Agent Analytics key (normally starts with ls_track_). Required. */\n  apiKey: string;\n\n  /** Override the track endpoint (for self-hosting). */\n  endpoint?: string;\n\n  /** Where the event was captured. Defaults to sdk. */\n  trackingSource?: \"sdk\" | \"cloudflare_worker\" | \"netlify_edge_proxy\";\n\n  /** Site builder/provider for no-code custom-domain setups. */\n  provider?: \"framer\" | \"webflow\" | \"netlify\" | \"static\" | \"custom\";\n\n  /** Apex domain used to ignore self-referrals across subdomains. */\n  siteDomain?: string;\n}\n```\n\n### Browser Identity Options\n\n```typescript\nimport { initVisitorIdentity } from \"@lightsage/agent-tracker/browser\";\n\nvoid initVisitorIdentity({\n  cookieDomain: \".example.com\", // optional\n  siteDomain: \"example.com\", // recommended when using subdomains\n  userId: \"user_123\", // optional, can also call identifyUser later\n  apiKey: \"ls_track_...\", // optional, only needed for direct browser conversions\n  conversionEndpoint: \"/api/sapient/conversion\", // optional first-party proxy\n  visitorMaxAgeDays: 365,\n  sessionMaxAgeMinutes: 30,\n  cookieSameSite: \"Lax\",\n});\n```\n\n### Environment Variables\n\nInstead of passing `apiKey` directly, you can set:\n\n```bash\nLIGHTSAGE_API_KEY=ls_track_...\n\n# Optional for Cloudflare Worker tracking\nSAPIENT_TRACKING_SOURCE=cloudflare_worker\n\n# Optional for Netlify DNS proxy tracking\nSAPIENT_TRACKING_SOURCE=netlify_edge_proxy\nORIGIN_URL=https://your-site.framer.website\nSITE_PROVIDER=framer\n```\n\n`SAPIENT_API_KEY` and `SAPIENT_TRACKING_KEY` remain deprecated compatibility\naliases for existing deployments. New integrations should use\n`LIGHTSAGE_API_KEY`.\n\nThen initialize without config:\n\n```typescript\n// Next.js\nimport { withAgentTracking } from \"@lightsage/agent-tracker/next\";\nexport default withAgentTracking({} as any); // reads from env\n```\n\n## Wrapping Existing Middleware\n\nAll platform wrappers accept an optional second argument to wrap existing middleware:\n\n### Next.js\n\n```typescript\nimport { withAgentTracking } from \"@lightsage/agent-tracker/next\";\nimport { NextResponse } from \"next/server\";\n\nfunction myMiddleware(request) {\n  // your existing logic\n  return NextResponse.next();\n}\n\nexport default withAgentTracking({ apiKey: \"ls_track_...\" }, myMiddleware);\n```\n\n### Express\n\n```typescript\n// Tracking middleware runs before your routes\napp.use(withAgentTracking({ apiKey: \"ls_track_...\" }));\napp.use(yourOtherMiddleware);\n```\n\n## How It Works\n\nThe middleware intercepts requests and:\n\n1. **Filters asset requests** — Sends page views (`Accept: text/html` or `text/markdown`) and any request from a non-browser client (a User-Agent not starting with `Mozilla/`), except static assets such as `.js`, `.css` and images\n2. **Classifies visitors** — Lightsage names the agent on its side from the User-Agent, headers, provider signatures and published IP ranges, so detection improves without upgrading the SDK\n3. **Identifies browsers** — Reads Lightsage visitor/session cookies produced by the browser helper\n4. **Preserves attribution** — Captures current, session, and first-touch attribution while ignoring configured same-site referrers\n5. **Sends async** — Tracking is non-blocking via `waitUntil()` or fire-and-forget\n6. **Auto-creates sites** — Sites are automatically registered when first event is received\n7. **Records outcomes** — Browser helpers can send explicit conversion milestones tied to the same visitor/user\n\n### What Gets Tracked\n\n| Field                    | Source                         |\n| ------------------------ | ------------------------------ |\n| `host`                   | Request hostname               |\n| `path`                   | Request pathname               |\n| `user_agent`             | User-Agent header              |\n| `accept`                 | Accept header                  |\n| `country`                | Geo header (platform-specific) |\n| `visitor_id`             | Browser identity cookie/header |\n| `session_id`             | Browser identity cookie/header |\n| `fingerprint_hash`       | Browser identity cookie/header |\n| `fingerprint_source`     | Browser identity cookie/header |\n| `fingerprint_confidence` | Browser identity cookie/header |\n| `external_user_id`       | Browser identity cookie/header |\n| `attribution_source`     | First-touch cookie/header      |\n| `attribution_medium`     | First-touch cookie/header      |\n| `attribution_campaign`   | First-touch cookie/header      |\n| `attribution_referrer`   | First-touch cookie/header      |\n| `attribution_landing_page` | First-touch cookie/header    |\n| `first_touch`            | First valid acquisition touch |\n| `session_touch`          | Source that began the session |\n| `current_touch`          | Source resolved from this request |\n| `method`                 | HTTP method                    |\n| `signature`, `signature_input`, `signature_agent` | Web Bot Auth headers, when present |\n| `signals`                | Request signals (below)        |\n\n#### Request signals\n\nUsed only to tell agents from people. The SDK never reads cookie,\nauthorization or other header values beyond this list.\n\n| Signal | Source |\n| ------ | ------ |\n| `header_names` | Names of the headers the client sent (no values), excluding headers added by your platform |\n| `sec_ch_ua`, `sec_ch_ua_platform` | Client hints, sent by Chromium browsers |\n| `sec_fetch_mode`, `sec_fetch_site`, `sec_fetch_dest` | Fetch metadata, sent by current browsers |\n| `accept_language`, `accept_encoding`, `dnt`, `sec_gpc` | Request headers |\n| `ja4` | TLS fingerprint from the platform: Vercel `x-vercel-ja4-digest`, Cloudflare Bot Management |\n| `asn` | Network of the client: Vercel `x-vercel-ip-as-number`, Cloudflare `request.cf.asn` |\n| `scheme` | `https` or `http` |\n\nLightsage stores signals only for requests it classifies as agents or bots.\n\n### Detection Logic\n\nDetection runs on Lightsage's side. Each agent visit is labelled by how its identity was established:\n\n| Label | Meaning | Examples |\n| ----- | ------- | -------- |\n| Verified | The provider vouched for the request | A Web Bot Auth signature (ChatGPT agent, Google-Agent), or an IP in the provider's published ranges (ChatGPT-User, Codex and Cursor cloud agents) |\n| Declared | The agent named itself in its User-Agent | Claude Code, Codex CLI, Gemini CLI, Qwen Code, Aider, Crush |\n| Inferred | Its headers match exactly one agent family | OpenCode's weighted `Accept` list, VS Code's fetch |\n\nClients that cannot be named are still counted: a command-line HTTP client\nreading docs, or a request whose User-Agent claims a browser but whose headers\nor TLS handshake no browser sends, is an unnamed coding agent on docs pages.\n\n## API Reference\n\n### Core Functions\n\n```typescript\nimport { trackVisit, init } from \"@lightsage/agent-tracker\";\n\n// Initialize globally (optional - can pass config per-call)\ninit({ apiKey: \"ls_track_...\" });\n\n// Track a visit manually\nconst result = await trackVisit({\n  host: \"docs.example.com\",\n  path: \"/getting-started\",\n  userAgent: \"...\",\n  accept: \"text/html,text/markdown\",\n  country: \"US\",\n  visitorId: \"visitor_...\",\n  sessionId: \"session_...\",\n  externalUserId: \"user_123\",\n  attributionSource: \"chatgpt\",\n  attributionCampaign: \"agent_analytics_demo\",\n});\n// => { ok: true, category: \"coding-agent\", agent: \"claude-code\" }\n```\n\n### Browser Functions\n\n```typescript\nimport {\n  clearUserIdentity,\n  getVisitorIdentity,\n  identifyUser,\n  initLightsagePixel,\n  initVisitorIdentity,\n  trackConversion,\n} from \"@lightsage/agent-tracker/browser\";\n\nconst identity = await getVisitorIdentity({ siteDomain: \"example.com\" });\n// => { visitorId, sessionId, firstTouch, sessionTouch, currentTouch, ... }\n\nvoid initVisitorIdentity(); // fire-and-forget cookie initializer\nawait identifyUser(\"user_123\");\nawait trackConversion(\n  \"signup.completed\",\n  { plan: \"pro\" },\n  { label: \"Complete sign up\", eventStage: \"completed\" },\n);\nvoid initLightsagePixel({ apiKey: \"ls_track_...\" }); // enables data-lightsage-conversion clicks\nclearUserIdentity();\n```\n\n### Platform Exports\n\nEach platform export includes:\n\n```typescript\n// Next.js\nimport {\n  withAgentTracking,\n  trackVisit,\n  init,\n} from \"@lightsage/agent-tracker/next\";\n\n// Vercel\nimport {\n  withAgentTracking,\n  trackRequest,\n  trackVisit,\n  init,\n} from \"@lightsage/agent-tracker/vercel\";\n\n// Cloudflare\nimport {\n  withAgentTracking,\n  onRequest,\n  trackRequest,\n  trackVisit,\n  init,\n} from \"@lightsage/agent-tracker/cloudflare\";\n\n// Netlify\nimport {\n  withAgentTracking,\n  proxyWithAgentTracking,\n  trackRequest,\n  trackProxyRequest,\n  trackVisit,\n  init,\n} from \"@lightsage/agent-tracker/netlify\";\n\n// Express\nimport {\n  withAgentTracking,\n  trackVisit,\n  init,\n} from \"@lightsage/agent-tracker/express\";\n\n// Browser\nimport {\n  clearUserIdentity,\n  getVisitorIdentity,\n  identifyUser,\n  initLightsagePixel,\n  initVisitorIdentity,\n  trackConversion,\n} from \"@lightsage/agent-tracker/browser\";\n```\n\n### Types\n\n```typescript\nimport type {\n  LightsageConfig,\n  TrackOptions,\n  TrackResult,\n} from \"@lightsage/agent-tracker\";\n\ninterface TrackResult {\n  ok: boolean;\n  category?: \"coding-agent\" | \"browsing-agent\" | \"bot\" | \"human\";\n  agent?: string;\n  skipped?: string; // \"not-page-view\" if filtered\n  error?: string;\n}\n```\n\nBrowser identity cookies can also be supplied as headers when you proxy requests:\n`x-sapient-visitor-id`, `x-sapient-session-id`,\n`x-sapient-fingerprint-hash`, `x-sapient-fingerprint-source`, and\n`x-sapient-fingerprint-confidence`.\n\n## Performance\n\n- **Non-blocking** — Uses `waitUntil()` on edge platforms, fire-and-forget on Express\n- **Timeout** — 2.5s max, silently fails on timeout\n- **Filtered early** — Asset requests (images, CSS, JS) are skipped before any network call\n\n## License\n\nMIT\n","type":"text"}]}
Thinking…

Good. So the SDK is real, docs are complete, and it uses a hosted API endpoint. The question is whether I can actually run something without an API key. trackVisit requires apiKey ls_track_.... I don't have credentials. But I can build the example and test it locally... well, "do not start local service stacks" — an Express server would be a local service. Better: use trackVisit directly (SDK function call), no server needed. But without a valid API key, the call will fail auth.

Let me check the endpoint and whether there's a docs API. Let me check what endpoint the SDK uses, and try a request to see the error shape. Also check the MCP package which needs an API key too.

Also, the "Public API" — maybe there's an api.lightsage.com with docs. Let's check.

Let me build a small example project in /sandbox/repo: a Node script using @lightsage/agent-tracker's trackVisit with LIGHTSAGE_API_KEY env, plus a Next.js/Express middleware snippet. And a verification run showing the classification result. Actually a nice demo without a key: the SDK's filtering logic can be exercised... but the classification happens server-side.

Let me test the endpoint response with a dummy key to see what happens.

cd /tmp/at/package/dist && grep -o "https://[a-zA-Z0-9./_-]*" *.mjs *.js chunk-*.mjs 2>/dev/null | sort -u | head -20
attribution.js:https://
attribution.js:https://
attribution.js:https://unknown.invalid
browser.js:https://
browser.js:https://ls.lightsage.com/api/v1/agent-tracking/conversion
browser.js:https://ls.lightsage.com/api/v1/agent-tracking/page-view
browser.js:https://unknown.invalid
browser.mjs:https://ls.lightsage.com/api/v1/agent-tracking/conversion
browser.mjs:https://ls.lightsage.com/api/v1/agent-tracking/page-view
chunk-VV3PAAWX.mjs:https://
chunk-VV3PAAWX.mjs:https://unknown.invalid
chunk-YMLOE6CX.mjs:https://ls.lightsage.com/api/v1/agent-tracking/track
cloudflare.js:https://
cloudflare.js:https://ls.lightsage.com/api/v1/agent-tracking/track
cloudflare.js:https://unknown.invalid
express.js:https://
express.js:https://ls.lightsage.com/api/v1/agent-tracking/track
express.js:https://unknown.invalid
index.js:https://
index.js:https://ls.lightsage.com/api/v1/agent-tracking/track
index.js:https://unknown.invalid
timeout 30 curl -sS -o /dev/null -w "%{http_code}\n" https://api.lightsage.com/api/v1/health; timeout 30 curl -sSL "https://api.lightsage.com/api/v1/agent-tracking/track" -X POST -H 'content-type: application/json' -d '{}' -w "\nHTTP %{http_code}\n" | head -20
404
404
{"detail":"Not Found"}
HTTP 404
Thinking…

The hosted endpoint is https://ls.lightsage.com/api/v1/agent-tracking/track. Let's test a POST with a dummy key to see what the API returns (does it accept and classify?).

timeout 30 curl -sS -X POST https://ls.lightsage.com/api/v1/agent-tracking/track -H 'content-type: application/json' -H 'authorization: Bearer ls_track_demo_invalid' -d '{"host":"docs.example.com","path":"/getting-started","user_agent":"claude-cli/1.0.60 (external, cli)","accept":"text/html"}' -w "\nHTTP %{http_code}\n"
{"detail":[{"type":"missing","loc":["header","x-api-key"],"msg":"Field required","input":null}]}
{"detail":[{"type":"missing","loc":["header","x-api-key"],"msg":"Field required","input":null}]}
HTTP 422
timeout 30 curl -sS https://ls.lightsage.com/api/v1/health -w "\nHTTP %{http_code}\n" | head; echo "=== openapi ==="; for p in /openapi.json /api/v1/openapi.json /docs /api/docs; do echo -n "$p "; timeout 15 curl -sS -o /dev/null -w "%{http_code}\n" "https://ls.lightsage.com$p"; done
{"detail":"Not Found"}
{"detail":"Not Found"}
HTTP 404
=== openapi ===
/openapi.json 404
/api/v1/openapi.json 200
/docs 200
/api/docs 404
Thinking…

There's an OpenAPI spec and Swagger docs at ls.lightsage.com. Let's fetch the openapi.json to know all endpoints (read-side API too: querying analytics data).

cd /tmp && timeout 60 curl -sS https://ls.lightsage.com/api/v1/openapi.json -o ls-openapi.json && python3 -c "
import json;d=json.load(open('ls-openapi.json'))
print(d['info'])
print('servers',d.get('servers'))
for p,ops in d['paths'].items():
    for m,o in ops.items():
        print(m.upper(),p,'-',o.get('summary'))
" | head -80
{'title': 'Welcome to Lightsage', 'description': 'Lightsage', 'version': '1.0.0'}
{'title': 'Welcome to Lightsage', 'description': 'Lightsage', 'version': '1.0.0'}
servers None
GET /api/v1/sandbox/stats - Sandbox Stats
POST /api/v1/sandbox/cleanup - Cleanup Sandboxes
POST /api/v1/sandbox/cleanup-all - Cleanup All Sandboxes Endpoint
POST /api/v1/sandbox/start - Start Sandbox
POST /api/v1/sandbox/stop - Stop Sandbox
POST /api/v1/sandbox/run - Run In Sandbox
POST /api/v1/sandbox/run-once - Run Once
POST /api/v1/snapshot/create - Create Snapshot
GET /api/v1/billing/balance - Get Balance
GET /api/v1/billing/transactions - Get Transactions
GET /api/v1/billing/usage - Get Usage
GET /api/v1/billing/usage/filter-options - Get Usage Filter Options
GET /api/v1/billing/usage/summary - Get Usage Summary
GET /api/v1/billing/invoices - Get Invoices
GET /api/v1/billing/payment-method - Get Payment Method
POST /api/v1/billing/checkout - Create Checkout
POST /api/v1/billing/topups/checkout - Create Topup Checkout
POST /api/v1/billing/check - Check Credits
POST /api/v1/billing/check_available_credits - Check Available Credits
POST /api/v1/billing/report-usage - Report Usage
POST /api/v1/billing/toggle-payg - Toggle Payg
GET /api/v1/billing/agent-run-limit - Get Agent Run Limit
PUT /api/v1/billing/agent-run-limit - Update Agent Run Limit
POST /api/v1/billing/portal - Create Portal
PUT /api/v1/billing/billing-email - Update Billing Email
POST /api/v1/billing/start-trial - Start Trial
GET /api/v1/billing/subscription-tiers - Get Subscription Tiers
POST /api/v1/billing/subscribe - Subscribe
POST /api/v1/billing/change-tier - Change Tier
POST /api/v1/billing/cancel-subscription - Cancel Subscription
POST /api/v1/billing/webhook - Stripe Webhook
GET /api/v1/platforms - List Platforms
POST /api/v1/onboarding/analyze - Analyze Brand
POST /api/v1/onboarding/research/competitors/stream - Stream Competitor Research
POST /api/v1/onboarding/research/prompts/stream - Stream Prompt Research
POST /api/v1/onboarding/email-allowed - Check Email Allowed
GET /api/v1/onboarding/team-organizations - List Team Organizations
POST /api/v1/onboarding/email-continue - Track Email Continue
POST /api/v1/onboarding/agent/verify-owner/start - Start Agent Owner Verification
POST /api/v1/onboarding/agent/verify-owner - Verify Agent Owner
GET /api/v1/onboarding/agent/api-key - Get Agent Api Key
POST /api/v1/onboarding/agent/setup - Setup Agent Workspace
POST /api/v1/onboarding/provision - Provision User
GET /api/v1/onboarding/teams - List Matching Teams
POST /api/v1/onboarding/teams/{org_id}/join-request - Request To Join Team
POST /api/v1/onboarding/brand - Create Brand Endpoint
GET /api/v1/onboarding/{brand_id}/competitors - Get Competitors
PUT /api/v1/onboarding/{brand_id}/competitors - Replace Onboarding Competitors
GET /api/v1/onboarding/{brand_id}/topics - Get Suggested Topics
POST /api/v1/onboarding/{brand_id}/generated-setup - Save Generated Setup
POST /api/v1/onboarding/{brand_id}/complete - Complete Onboarding
POST /api/v1/agent-receipts/signups - Create Signup
GET /api/v1/agent-receipts/leaderboard - Get Leaderboard
GET /api/v1/agent-receipts/by-domain/{domain} - Get Receipt For Domain
GET /api/v1/agent-receipts/{receipt_id} - Get Receipt
GET /api/v1/agent-receipts/{receipt_id}/events - Get Receipt Run Events
POST /api/v1/agent-receipts - Create Receipt
GET /api/v1/setup-guide/status - Read Setup Guide Status
POST /api/v1/setup-guide/reviews - Record Setup Guide Review
GET /api/v1/brands - List Brands
GET /api/v1/brands/suggested - List Suggested Competitors
GET /api/v1/brands/mention-reanalysis/status - Get Reanalysis Status
GET /api/v1/brands/{org_brand_id} - Get Brand
POST /api/v1/brands/competitors - Add Competitor
PATCH /api/v1/brands/competitors/{org_brand_id} - Update Competitor
DELETE /api/v1/brands/competitors/{org_brand_id} - Remove Competitor
GET /api/v1/brands/competitors/{org_brand_id}/historical-data - Get Competitor Historical Data
PUT /api/v1/brands/{org_brand_id}/aliases - Update Aliases
GET /api/v1/brand-kits - List Brand Kits
POST /api/v1/brand-kits - Create Brand Kit Endpoint
GET /api/v1/brand-kits/{brand_kit_id} - Get Brand Kit Detail
PUT /api/v1/brand-kits/{brand_kit_id} - Update Brand Kit Endpoint
DELETE /api/v1/brand-kits/{brand_kit_id} - Delete Brand Kit Endpoint
POST /api/v1/brand-kits/{brand_kit_id}/regenerate - Regenerate Brand Kit
POST /api/v1/brand-kits/{brand_kit_id}/set-default - Set Default Brand Kit
POST /api/v1/brand-kits/{brand_kit_id}/duplicate - Duplicate Brand Kit Endpoint
POST /api/v1/prompts/generate-keywords - Generate Keywords
POST /api/v1/prompts/generate - Generate Prompts
Traceback (most recent call last):
  File "<string>", line 7, in <module>
BrokenPipeError: [Errno 32] Broken pipe
Thinking…

Big API. Let's look specifically at agent-tracking endpoints and their auth requirements, plus any public/no-auth endpoints (e.g., agent-receipts leaderboard might be public). That would let me build a working example without credentials.

Let me grep the openapi for agent-tracking paths and check security schemes.

cd /tmp && python3 -c "
import json;d=json.load(open('ls-openapi.json'))
print('paths total',len(d['paths']))
print('security schemes',list(d.get('components',{}).get('securitySchemes',{}).items())[:5])
for p,ops in d['paths'].items():
    if 'agent-tracking' in p or 'public' in p:
        for m,o in ops.items():
            print(m.upper(),p,'|',o.get('summary'),'| security:',o.get('security'))
"
paths total 870
paths total 870
security schemes []
POST /api/v1/agent-tracking/track | Track Event | security: None
POST /api/v1/agent-tracking/conversion | Track a conversion event | security: None
POST /api/v1/agent-tracking/page-view | Track a browser page view | security: None
GET /api/v1/agent-tracking/detect | Detect Visitor | security: None
GET /api/v1/agent-tracking/hosting-detect | Hosting Detect | security: None
GET /api/v1/agent-tracking/api-key | Get Api Key | security: None
POST /api/v1/agent-tracking/api-key/regenerate | Regenerate Api Key | security: None
GET /api/v1/agent-tracking/attribution | Get User Attribution | security: None
GET /api/v1/agent-tracking/sites | List Sites | security: None
GET /api/v1/agent-tracking/sites/{site_id} | Get Site | security: None
DELETE /api/v1/agent-tracking/sites/{site_id} | Delete Site | security: None
POST /api/v1/agent-tracking/sites/bulk-delete | Bulk Delete Sites | security: None
GET /api/v1/agent-tracking/site-stats | Get Sites Stats | security: None
GET /api/v1/agent-tracking/sites/{site_id}/stats | Get Site Stats | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agents | Get Site Agents | security: None
GET /api/v1/agent-tracking/sites/{site_id}/bots | Get Site Bots | security: None
GET /api/v1/agent-tracking/sites/{site_id}/ai-bots | Get Site Ai Bots | security: None
GET /api/v1/agent-tracking/sites/{site_id}/ai-bot-pages | Get Site Ai Bot Pages | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agent-traffic/summary | Get Site Agent Traffic Summary | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agent-traffic/pages | Get Site Agent Traffic Pages | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agent-traffic/sources-over-time | Get Site Ai Source Visits Over Time | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agent-traffic/mix | Get Site Agent Traffic Mix | security: None
GET /api/v1/agent-tracking/sites/{site_id}/pages | Get Site Pages | security: None
GET /api/v1/agent-tracking/sites/{site_id}/feed | Get Site Feed | security: None
GET /api/v1/agent-tracking/sites/{site_id}/tracking-sources | Get Site Tracking Sources | security: None
GET /api/v1/agent-tracking/sites/{site_id}/conversion-setup | Get Site Conversion Setup | security: None
GET /api/v1/agent-tracking/sites/{site_id}/conversions | Get Site Conversions | security: None
GET /api/v1/agent-tracking/sites/{site_id}/coding-agents/funnel | Get Coding Agent Funnel | security: None
GET /api/v1/agent-tracking/sites/{site_id}/ai-conversion-funnel | Get Ai Conversion Funnel | security: None
GET /api/v1/agent-tracking/sites/{site_id}/coding-agents/sessions | List Coding Agent Sessions | security: None
GET /api/v1/agent-tracking/sites/{site_id}/coding-agents/sessions/{session_id} | Get Coding Agent Session | security: None
GET /api/v1/agent-tracking/sites/{site_id}/live-events | Get Site Live Events | security: None
GET /api/v1/agent-tracking/sites/{site_id}/visits-over-time | Get Site Visits Over Time | security: None
GET /api/v1/agent-tracking/sites/{site_id}/path-visits | Get Site Path Visits | security: None
GET /api/v1/agent-tracking/sites/{site_id}/countries | Get Site Countries | security: None
GET /api/v1/agent-tracking/sites/{site_id}/agent-visits-over-time | Get Site Agent Visits Over Time | security: None
GET /api/v1/agent-tracking/sites/{site_id}/status-latency-stats | Get Site Status Latency Stats | security: None
GET /api/v1/agent-tracking/sites/{site_id}/error-requests | Get Site Error Requests | security: None
GET /api/v1/agent-tracking/ignored-domains | Get Ignored Domains | security: None
PUT /api/v1/agent-tracking/ignored-domains | Update Ignored Domains | security: None
GET /api/v1/agent-tracking/sites/{site_id}/funnel-order | Get Funnel Order | security: None
PUT /api/v1/agent-tracking/sites/{site_id}/funnel-order | Update Funnel Order | security: None
POST /api/v1/agent-tracking/scan-url | Scan Url | security: None
POST /api/v1/agent-tracking/first-visit | Send First Visit | security: None
GET /api/v1/agent-tracking/integrations/mintlify | Get Mintlify Status | security: None
PATCH /api/v1/agent-tracking/integrations/mintlify | Update Mintlify | security: None
POST /api/v1/agent-tracking/integrations/mintlify/connect | Connect Mintlify | security: None
POST /api/v1/agent-tracking/integrations/mintlify/disconnect | Disconnect Mintlify | security: None
GET /api/v1/agent-tracking/integrations/mintlify/views | Get Mintlify Views | security: None
GET /api/v1/agent-tracking/integrations/mintlify/visitors | Get Mintlify Visitors | security: None
GET /api/v1/agent-tracking/integrations/mintlify/time-series | Get Mintlify Time Series | security: None
GET /api/v1/agent-tracking/integrations/mintlify/path-time-series | Get Mintlify Path Time Series | security: None
GET /api/v1/agent-tracking/integrations/mintlify/visitors-time-series | Get Mintlify Visitors Time Series | security: None
GET /api/v1/agent-tracking/integrations/vercel-log-drain | Get Vercel Log Drain Status | security: None
POST /api/v1/agent-tracking/integrations/vercel-log-drain/connect | Connect Vercel Log Drain | security: None
POST /api/v1/agent-tracking/integrations/vercel-log-drain/disconnect | Disconnect Vercel Log Drain | security: None
POST /api/v1/agent-tracking/vercel-webhook | Receive Vercel Log Drain | security: None
GET /api/v1/agent-tracking/integrations/cloudfront-firehose | Get Cloudfront Firehose Status | security: None
POST /api/v1/agent-tracking/integrations/cloudfront-firehose/connect | Connect Cloudfront Firehose | security: None
POST /api/v1/agent-tracking/integrations/cloudfront-firehose/disconnect | Disconnect Cloudfront Firehose | security: None
POST /api/v1/agent-tracking/cloudfront-firehose | Receive Cloudfront Firehose | security: None
GET /api/v1/publications | List Publications | security: None
GET /api/v1/publications/{output_id}/page | Publication Page | security: None
POST /api/v1/publications/review/check | Check Content In Review | security: None
GET /api/v1/publications/link-title | Link Title | security: None
POST /api/v1/publications/links | Add Link | security: None
POST /api/v1/publications/{publication_id}/publish | Mark Published | security: None
GET /api/v1/publications/{publication_id} | Get Publication | security: None
GET /api/v1/fixes/content/{fix_id}/publications | List Content Publications | security: None
POST /api/v1/fixes/content/{fix_id}/publications | Create Content Publication | security: None
PATCH /api/v1/fixes/content/{fix_id}/publications/{publication_id} | Update Content Publication | security: None
GET /api/v1/admin/public-api-usage | Get Public Api Usage | security: None
GET /api/v1/agent-payments/public/companies | List published Agent Payments Arena companies | security: None
GET /api/v1/agent-payments/public/companies/{company_slug} | Get a published Agent Payments Arena company report | security: None
POST /api/v1/agent-payments/public/submissions | List a self-submitted company in the Agent Payments Arena | security: None
POST /api/v1/agent-payments/public/static-scan-results | Save a public agent-payment readiness scan | security: None
POST /api/v1/agent-payments/public/static-scan | Run a public agent-payment readiness scan | security: None
Thinking…

Great. Key finding: GET /api/v1/agent-tracking/detect — Detect Visitor — likely takes a user agent and returns classification, and may not require auth or requires api key. Let's check its params. Also GET /api/v1/agent-tracking/api-key probably requires session auth.

Let me examine detect and hosting-detect schemas and try them.

Also check whether there's a docs site for the public API (docs on lightsage.com). Let's check /api/v1/agent-tracking/detect with a user agent.

cd /tmp && python3 -c "
import json;d=json.load(open('ls-openapi.json'))
for p in ['/api/v1/agent-tracking/detect','/api/v1/agent-tracking/hosting-detect','/api/v1/agent-tracking/track']:
    print('=====',p)
    print(json.dumps(d['paths'][p],indent=1)[:2500])
"
===== /api/v1/agent-tracking/detect
===== /api/v1/agent-tracking/detect
{
 "get": {
  "tags": [
   "agent-tracking"
  ],
  "summary": "Detect Visitor",
  "description": "Classify a visitor without recording. Useful for testing.\nNo auth required.",
  "operationId": "detect_visitor_api_v1_agent_tracking_detect_get",
  "parameters": [
   {
    "name": "user_agent",
    "in": "query",
    "required": false,
    "schema": {
     "type": "string",
     "default": "",
     "title": "User Agent"
    }
   },
   {
    "name": "accept",
    "in": "query",
    "required": false,
    "schema": {
     "type": "string",
     "default": "",
     "title": "Accept"
    }
   },
   {
    "name": "host",
    "in": "query",
    "required": false,
    "schema": {
     "type": "string",
     "default": "",
     "title": "Host"
    }
   }
  ],
  "responses": {
   "200": {
    "description": "Successful Response",
    "content": {
     "application/json": {
      "schema": {}
     }
    }
   },
   "422": {
    "description": "Validation Error",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/HTTPValidationError"
      }
     }
    }
   }
  }
 }
}
===== /api/v1/agent-tracking/hosting-detect
{
 "get": {
  "tags": [
   "agent-tracking"
  ],
  "summary": "Hosting Detect",
  "description": "Detect hosting provider, CDN, and DNS for a domain.\nReturns recommended integration path based on detected stack.\nRate limited to prevent abuse.",
  "operationId": "hosting_detect_api_v1_agent_tracking_hosting_detect_get",
  "parameters": [
   {
    "name": "domain",
    "in": "query",
    "required": true,
    "schema": {
     "type": "string",
     "description": "Domain to detect hosting for (e.g., example.com)",
     "title": "Domain"
    },
    "description": "Domain to detect hosting for (e.g., example.com)"
   },
   {
    "name": "authorization",
    "in": "header",
    "required": false,
    "schema": {
     "anyOf": [
      {
       "type": "string"
      },
      {
       "type": "null"
      }
     ],
     "title": "Authorization"
    }
   },
   {
    "name": "x-clerk-auth-token",
    "in": "header",
    "required": false,
    "schema": {
     "anyOf": [
      {
       "type": "string"
      },
      {
       "type": "null"
      }
     ],
     "title": "X-Clerk-Auth-Token"
    }
   },
   {
    "name": "x-org-id",
    "in": "header",
    "required": false,
    "schema": {
     "anyOf": [
      {
       "type": "string"
      },
      {
       "type": "null"
      }
     ],
     "title": "X-Org-Id"
    }
   }
  ],
  "responses": {
   "200": {
    "description": "Successful Response",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/HostingDetectResponse"
      }
     }
    }
   },
   "422": {
    "description": "Validation Error",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/HTTPValidationError"
      }
     }
    }
   }
  }
 }
}
===== /api/v1/agent-tracking/track
{
 "post": {
  "tags": [
   "agent-tracking"
  ],
  "summary": "Track Event",
  "description": "Record a tracking event. Called from customer middleware.\nAuthenticates via the x-api-key header. Existing Unkey-issued keys remain\nvalid through the migration fallback.\nAuto-creates sites from the host domain.",
  "operationId": "track_event_api_v1_agent_tracking_track_post",
  "parameters": [
   {
    "name": "x-api-key",
    "in": "header",
    "required": true,
    "schema": {
     "type": "string",
     "title": "X-Api-Key"
    }
   }
  ],
  "requestBody": {
   "required": true,
   "content": {
    "application/json": {
     "schema": {
      "$ref": "#/components/schemas/TrackEventRequest"
     }
    }
   }
  },
  "responses": {
   "200": {
    "description": "Successful Response",
    "content": {
     "application/json": {
      "schema": {}
     }
    }
   },
   "422": {
    "description": "Validation Error",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/HTTPValidationError"
      }
     }
    }
   }
  }
 }
}
Thinking…

Excellent: /detect needs no auth — I can build a genuinely runnable example with zero credentials. And hosting-detect too. That's the perfect "simple example":

  1. A script that classifies user agents via the no-auth /detect endpoint (real hosted API, no key needed).
  2. A tracking example using the SDK trackVisit with LIGHTSAGE_API_KEY env (works when key provided, gracefully reports when not).
  3. Middleware snippets for Express/Next.js (files, not run).

Let me test detect quickly.

cd /tmp && for ua in "claude-cli/1.0.60 (external, cli)" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120 Safari/537.36" "curl/8.4.0" "Codex CLI/0.20"; do echo "--- $ua"; timeout 20 curl -sS -G "https://ls.lightsage.com/api/v1/agent-tracking/detect" --data-urlencode "user_agent=$ua" --data-urlencode "accept=text/html,text/markdown" | head -c 400; echo; done
--- claude-cli/1.0.60 (external, cli)
--- claude-cli/1.0.60 (external, cli)
{"category":"coding-agent","agent":"unknown-coding-agent","bot_purpose":null,"bot_name":null,"is_page_view":true}
--- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120 Safari/537.36
{"category":"coding-agent","agent":"unknown-coding-agent","bot_purpose":null,"bot_name":null,"is_page_view":true}
--- curl/8.4.0
{"category":"bot","agent":"curl/","bot_purpose":null,"bot_name":null,"is_page_view":true}
--- Codex CLI/0.20
{"category":"coding-agent","agent":"codex","bot_purpose":null,"bot_name":null,"is_page_view":true}
cd /tmp && timeout 30 curl -sS -G "https://ls.lightsage.com/api/v1/agent-tracking/hosting-detect" --data-urlencode "domain=lightsage.com" | python3 -m json.tool | head -40
{
{
    "detail": "Missing authorization header"
}
Thinking…

/detect works with no auth. Note: Chrome UA classified as coding-agent "unknown-coding-agent" — probably because missing browser-ish headers (no sec-ch-ua etc.). Fine; I'll include full browser headers in my example... but detect only accepts user_agent/accept/host. So browser UA gets misclassified when only UA given. Interesting caveat worth noting.

Actually maybe host matters. Whatever.

Now build the example in /sandbox/repo:

  • package.json with dependency @lightsage/agent-tracker
  • examples/detect.mjs — no-key demo hitting hosted detect endpoint
  • examples/track.mjs — SDK trackVisit with LIGHTSAGE_API_KEY
  • examples/express-middleware.mjs — file only, not run
  • middleware.ts for Next.js
  • README.md with pricing summary and notes

Let me install the package in the repo and run the detect example to verify. Installing npm package is fine (not a service stack).

Let me check what trackVisit does without a key and with a bad key — I can run it to show graceful failure. Let me write files.

First install.

cd /sandbox/repo && cat .gitignore && npm init -y >/dev/null 2>&1 && timeout 180 npm install @lightsage/agent-tracker 2>&1 | tail -3 && node -e "console.log(process.version)"
# Keep generated and vendored files out of source diffs.
# Keep generated and vendored files out of source diffs.

# Logs / OS cruft
*.log
.DS_Store

# JavaScript / TypeScript
node_modules/
.next/
dist/
build/
coverage/

# Python
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
*.egg-info/

# Rust
target/

# Go / PHP / Ruby vendored deps
vendor/
.bundle/

# Java / Kotlin / Scala
.gradle/

# .NET
bin/
obj/

# Elixir
_build/
deps/

# Dart / Flutter
.dart_tool/
added 1 package, and audited 2 packages in 776ms

found 0 vulnerabilities
v22.23.4
Thinking…

Zero-dependency package, nice. Now write files.

Let me check the detect SDK function signature in dist to see if there's a helper for detect (probably not). I'll use fetch.

Write:

  • examples/01-detect.mjs (no key needed)
  • examples/02-track-visit.mjs (needs LIGHTSAGE_API_KEY)
  • examples/03-express-middleware.mjs (snippet; guard so it doesn't run unless asked? It would start a server — instruction says don't start local service stacks. I'll keep it as a file with instructions, not run it.)
  • middleware.ts (Next.js snippet)
  • README.md

Let me check TrackEventRequest schema fields for trackVisit mapping (SDK handles it).

Now write.

File edits

1 file

Completed
Writtenexamples/01-detect.mjsView changes

/sandbox/repo/examples/01-detect.mjs

Written content · previous contents were not recorded

#!/usr/bin/env node
/**
* Lightsage example 1 — classify a visitor WITHOUT an API key.
*
* Uses the hosted, public "detect" endpoint of the Lightsage API:
* GET https://ls.lightsage.com/api/v1/agent-tracking/detect
* "Classify a visitor without recording. Useful for testing. No auth required."
*
* Nothing is recorded and no local services are started.
*
* Run: node examples/01-detect.mjs
*/
const DETECT_URL = "https://ls.lightsage.com/api/v1/agent-tracking/detect";
const SAMPLES = [
{
label: "Claude Code reading your docs",
user_agent: "claude-cli/1.0.60 (external, cli)",
accept: "text/html,text/markdown;q=0.9,*/*;q=0.8",
},
{
label: "OpenAI Codex CLI",
user_agent: "Codex CLI/0.20.0 (linux; node 22)",
accept: "text/markdown,text/plain;q=0.9",
},
{
label: "Generic command-line HTTP client",
user_agent: "curl/8.4.0",
accept: "*/*",
},
{
label: "ChatGPT-User (browsing agent)",
user_agent:
"Mozilla/5.0 AppleWebKit/537.36; compatible; ChatGPT-User/1.0; +https://openai.com/gptbot",
accept: "text/html,application/xhtml+xml",
},
{
label: "Googlebot (search crawler, not an agent)",
user_agent:
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
accept: "text/html",
},
];
async function detect({ user_agent, accept, host = "docs.example.com" }) {
const url = new URL(DETECT_URL);
url.searchParams.set("user_agent", user_agent);
url.searchParams.set("accept", accept);
url.searchParams.set("host", host);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`detect failed: HTTP ${res.status} ${await res.text()}`);
}
return res.json();
}
const rows = [];
for (const sample of SAMPLES) {
const result = await detect(sample).catch((err) => ({ error: String(err) }));
rows.push({ what: sample.label, ...result });
}
console.log("Lightsage visitor classification (live, no API key)\n");
console.table(
rows.map((r) => ({
visitor: r.what,
category: r.category ?? "-",
agent: r.agent ?? "-",
page_view: r.is_page_view ?? "-",
...(r.error ? { error: r.error } : {}),
})),
);
console.log(
"\nNote: `category` is one of coding-agent | browsing-agent | bot | human.",
);
console.log(
"Classification happens on Lightsage's side, so detection improves without an SDK upgrade.",
);

File edits

1 file

Completed
Writtenexamples/02-track-visit.mjsView changes

/sandbox/repo/examples/02-track-visit.mjs

Written content · previous contents were not recorded

#!/usr/bin/env node
/**
* Lightsage example 2 — record real agent visits with the SDK.
*
* This is the same call the Express/Next.js/Cloudflare middleware makes on
* every request, extracted into a single script so it can be run on its own
* (no web server, no local stack).
*
* Requires an Agent Analytics key from the Lightsage dashboard:
* export LIGHTSAGE_API_KEY=ls_track_...
* node examples/02-track-visit.mjs
*
* Without a key the script exits with a clear message instead of guessing.
*/
import { init, trackVisit } from "@lightsage/agent-tracker";
const apiKey = process.env.LIGHTSAGE_API_KEY;
if (!apiKey) {
console.error(
[
"LIGHTSAGE_API_KEY is not set.",
"",
"1. Create an account at https://lightsage.com (Free plan, no card).",
"2. Open Agent Analytics -> API key. Keys start with `ls_track_`",
" (older Unkey-issued `sap_` keys still work).",
"3. export LIGHTSAGE_API_KEY=ls_track_... and re-run this script.",
"",
"Run examples/01-detect.mjs meanwhile — it needs no key.",
].join("\n"),
);
process.exit(1);
}
// Optional: set the key once instead of passing it on every call.
init({ apiKey });
const visits = [
{
host: "docs.example.com",
path: "/getting-started",
userAgent: "claude-cli/1.0.60 (external, cli)",
accept: "text/html,text/markdown;q=0.9",
country: "US",
visitorId: "visitor_demo_1",
sessionId: "session_demo_1",
attributionSource: "chatgpt",
attributionCampaign: "agent_analytics_demo",
},
{
host: "docs.example.com",
path: "/api-reference",
userAgent: "Codex CLI/0.20.0 (linux; node 22)",
accept: "text/markdown,text/plain;q=0.9",
visitorId: "visitor_demo_2",
sessionId: "session_demo_2",
},
{
// Static assets are filtered out before any network call.
host: "docs.example.com",
path: "/assets/app.css",
userAgent: "Mozilla/5.0 (Macintosh) AppleWebKit/537.36 Chrome/120 Safari/537.36",
accept: "text/css,*/*;q=0.1",
},
];
for (const visit of visits) {
// trackVisit resolves with { ok, category, agent, skipped?, error? }.
// It never throws on network failure (2.5s timeout, silent failure).
const result = await trackVisit(visit);
console.log(`${visit.path.padEnd(20)} ->`, result);
}
console.log(
"\nSites are auto-created from the host on the first event, then appear in",
"the Lightsage dashboard under Agent Analytics.",
);
Thinking…

Now the middleware snippets: express and next. Also a package.json script section. And README.

Let me write examples/03-express-middleware.mjs and middleware.ts.

File edits

1 file

Completed
Writtenexamples/03-express-middleware.mjsView changes

/sandbox/repo/examples/03-express-middleware.mjs

Written content · previous contents were not recorded

/**
* Lightsage example 3 — drop-in Express middleware (reference snippet).
*
* Not run automatically: it is a web server, and this repo keeps things light.
* To try it:
* npm install express
* export LIGHTSAGE_API_KEY=ls_track_...
* node examples/03-express-middleware.mjs
* curl -A "claude-cli/1.0.60 (external, cli)" http://localhost:3000/getting-started
*
* Equivalent one-liners for other platforms:
*
* // Next.js — middleware.ts
* import { withAgentTracking } from "@lightsage/agent-tracker/next";
* export default withAgentTracking({ apiKey: process.env.LIGHTSAGE_API_KEY });
* export const config = { matcher: ["/((?!api|_next|admin).*)"] };
*
* // Cloudflare Workers
* import { withAgentTracking } from "@lightsage/agent-tracker/cloudflare";
* export default { fetch: withAgentTracking({}, handler.fetch) };
*
* // Netlify DNS proxy in front of Framer/Webflow
* import { proxyWithAgentTracking } from "@lightsage/agent-tracker/netlify";
* export default proxyWithAgentTracking();
* export const config = { path: "/*" };
*/
import express from "express";
import { withAgentTracking } from "@lightsage/agent-tracker/express";
const app = express();
// Non-blocking: fires the tracking event and returns immediately.
app.use(withAgentTracking({ apiKey: process.env.LIGHTSAGE_API_KEY }));
app.get("/getting-started", (_req, res) => {
res.type("text/markdown").send("# Getting started\n\nHello, humans and agents.");
});
app.listen(3000, () => console.log("http://localhost:3000"));

File edits

1 file

Completed
Writtenexamples/04-browser-conversions.tsView changes

/sandbox/repo/examples/04-browser-conversions.ts

Written content · previous contents were not recorded

import {
identifyUser,
initLightsagePixel,
trackConversion,
} from "@lightsage/agent-tracker/browser";
/**
* Lightsage example 4 — browser side: visitor identity + conversions.
*
* Copy this into your client app (it is written as a module, not run here).
* Conversions are how an agent visit turns into attributed revenue:
* the middleware records the visit, this records the outcome for the same
* visitor.
*
* `label` and `eventStage` are required on trackConversion.
*/
// 1. Identify repeat human visitors (first-party fingerprint + cookies).
void initLightsagePixel({
apiKey: import.meta.env.VITE_LIGHTSAGE_KEY, // browser-safe tracking key
siteDomain: "example.com",
sessionMaxAgeMinutes: 30,
visitorMaxAgeDays: 365,
});
// 2. After login/signup, attach YOUR user id (not a Lightsage account id).
export function onLogin(userId: string) {
void identifyUser(userId);
}
// 3. Record outcomes as they happen.
export function onSignupCompleted(plan: "free" | "pro") {
void trackConversion(
"signup.completed",
{ plan },
{
apiKey: import.meta.env.VITE_LIGHTSAGE_KEY,
label: "Complete sign up",
eventStage: "completed",
},
);
}
// Declarative alternative for CTA clicks (no JS needed):
//
// <script type="module" src="/lightsage-browser.mjs"
// data-lightsage-key="ls_track_..."></script>
// <button data-lightsage-conversion="signup.click"
// data-lightsage-conversion-stage="intent"
// data-lightsage-label="Sign up">Sign up</button>
Thinking…

Verify initLightsagePixel signature — README says void initLightsagePixel({ apiKey: "ls_track_..." }); // enables data-lightsage-conversion clicks and initVisitorIdentity takes options including siteDomain/sessionMaxAgeMinutes. Let me check the d.ts to make sure initLightsagePixel accepts those options.

cd /sandbox/repo/node_modules/@lightsage/agent-tracker/dist && cat browser.d.ts | head -80
import { VisitorIdentityContext } from './index.js';
import { VisitorIdentityContext } from './index.js';
export { AttributionReason, SapientAttributionContext, SapientAttributionResolution, SapientAttributionTouch } from './attribution.js';

type TrackingSource = "sdk" | "cloudflare_worker" | "netlify_edge_proxy";
type SiteProvider = "framer" | "webflow" | "netlify" | "static" | "custom";
type ConversionStage = "intent" | "completed";
type ConversionProperties = Record<string, unknown>;
interface VisitorIdentityOptions {
    /** Refresh the visitor ID even when a stored value exists. */
    forceRefresh?: boolean;
    /** Write identity values to localStorage/sessionStorage. Defaults to true. */
    storage?: boolean;
    /** Write identity values to first-party cookies for server middleware. Defaults to true. */
    cookies?: boolean;
    /** Cookie domain. Omit to use the current host only. */
    cookieDomain?: string;
    /** Cookie path. Defaults to /. */
    cookiePath?: string;
    /** SameSite value for identity cookies. Defaults to Lax. */
    cookieSameSite?: "Lax" | "Strict" | "None";
    /** Set Secure on cookies. Defaults to true on HTTPS pages. */
    cookieSecure?: boolean;
    /** Visitor cookie lifetime. Defaults to 365 days. */
    visitorMaxAgeDays?: number;
    /** Rolling session cookie lifetime. Defaults to 30 minutes. */
    sessionMaxAgeMinutes?: number;
    /** Storage key prefix. Defaults to sapient. */
    storagePrefix?: string;
    /** Customer application user ID to persist when the app knows it. */
    userId?: string;
    /** Capture and persist first-touch URL/referrer attribution. Defaults to true. */
    attribution?: boolean;
    /** Refresh stored first-touch attribution from the current page. Defaults to false. */
    forceAttributionRefresh?: boolean;
    /** Apex domain used to ignore self-referrals across subdomains. */
    siteDomain?: string;
    /** Include canvas rendering signals. Defaults to true. */
    canvas?: boolean;
    /** Include WebGL vendor/renderer signals. Defaults to true. */
    webgl?: boolean;
    /** Include OfflineAudioContext rendering signal. Defaults to true. */
    audio?: boolean;
    /** Include a lightweight installed-font probe. Defaults to true. */
    fonts?: boolean;
    /** Override the fonts checked by the font probe. */
    fontList?: string[];
    /** Log collector failures to the console. Defaults to false. */
    debug?: boolean;
    /** Lightsage tracking key used by browser conversion events. */
    apiKey?: string;
    /** Override the browser conversion endpoint. */
    conversionEndpoint?: string;
    /** Override the browser page-view endpoint. */
    pageViewEndpoint?: string;
    /** Where browser conversion events originated. Defaults to SDK tracking. */
    trackingSource?: TrackingSource;
    /** Website platform provider for no-code/custom-domain setups. */
    provider?: SiteProvider;
}
interface LightsageVisitorIdentity extends VisitorIdentityContext {
    visitorId: string;
    sessionId: string;
}
/** @deprecated Use LightsageVisitorIdentity. */
type SapientVisitorIdentity = LightsageVisitorIdentity;
interface ConversionRequestContextOptions extends VisitorIdentityOptions {
    /** Idempotency key. Generated automatically when omitted. */
    eventId?: string;
    /** Transport used for the event. Defaults to beacon with fetch fallback. */
    transport?: "auto" | "beacon" | "fetch";
    /** Override host/path/url/referrer when tracking from custom routers or proxies. */
    host?: string;
    path?: string;
    url?: string;
    referrer?: string;
    userAgent?: string;
}
interface TrackConversionOptions extends ConversionRequestContextOptions {
    /** Human-readable label shown in analytics. */
    label: string;
cd /sandbox/repo && node examples/01-detect.mjs
Lightsage visitor classification (live, no API key)
Lightsage visitor classification (live, no API key)

┌─────────┬────────────────────────────────────────────┬──────────────────┬────────────────────────┬───────────┐
│ (index) │ visitor                                    │ category         │ agent                  │ page_view │
├─────────┼────────────────────────────────────────────┼──────────────────┼────────────────────────┼───────────┤
│ 0       │ 'Claude Code reading your docs'            │ 'coding-agent'   │ 'unknown-coding-agent' │ true      │
│ 1       │ 'OpenAI Codex CLI'                         │ 'coding-agent'   │ 'codex'                │ true      │
│ 2       │ 'Generic command-line HTTP client'         │ 'bot'            │ 'curl/'                │ false     │
│ 3       │ 'ChatGPT-User (browsing agent)'            │ 'browsing-agent' │ 'chatgpt'              │ true      │
│ 4       │ 'Googlebot (search crawler, not an agent)' │ 'bot'            │ 'googlebot'            │ true      │
└─────────┴────────────────────────────────────────────┴──────────────────┴────────────────────────┴───────────┘

Note: `category` is one of coding-agent | browsing-agent | bot | human.
Classification happens on Lightsage's side, so detection improves without an SDK upgrade.
Thinking…

The detect example works live. Let me fix example 04: initLightsagePixel — check its signature in browser.d.ts (does it accept VisitorIdentityOptions?).

Also verify trackVisit works without key (exit path) — test example 02 without key.

cd /sandbox/repo/node_modules/@lightsage/agent-tracker/dist && grep -n "initLightsagePixel\|initVisitorIdentity\|declare function trackConversion\|trackPageView" browser.d.ts
117: trackPageView: (options?: ConversionRequestContextOptions) => Promise<TrackPageViewResult>;
117:    trackPageView: (options?: ConversionRequestContextOptions) => Promise<TrackPageViewResult>;
136:declare function initVisitorIdentity(options?: VisitorIdentityOptions): Promise<LightsageVisitorIdentity>;
153:declare function trackConversion(eventName: string, properties: ConversionProperties | undefined, options: TrackConversionOptions): Promise<TrackConversionResult>;
161:declare function trackPageView(options?: ConversionRequestContextOptions): Promise<TrackPageViewResult>;
166:declare function initLightsagePixel(options?: LightsagePixelOptions): Promise<LightsageVisitorIdentity>;
167:/** @deprecated Use initLightsagePixel. */
168:declare const initSapientPixel: typeof initLightsagePixel;
170:export { type LightsageBrowserApi, type LightsagePixelOptions, type LightsageVisitorIdentity, type SapientBrowserApi, type SapientPixelOptions, type SapientVisitorIdentity, type TrackConversionOptions, type TrackConversionResult, type TrackPageViewResult, type VisitorIdentityOptions, clearUserIdentity, getVisitorIdentity, identifyUser, initLightsagePixel, initSapientPixel, initVisitorIdentity, trackConversion, trackPageView };
cd /sandbox/repo && node examples/02-track-visit.mjs; echo "exit=$?"; echo "--- with bogus key ---"; LIGHTSAGE_API_KEY=ls_track_demo_bogus node examples/02-track-visit.mjs; echo "exit=$?"
LIGHTSAGE_API_KEY is not set.
LIGHTSAGE_API_KEY is not set.

1. Create an account at https://lightsage.com (Free plan, no card).
2. Open Agent Analytics -> API key. Keys start with `ls_track_`
   (older Unkey-issued `sap_` keys still work).
3. export LIGHTSAGE_API_KEY=ls_track_... and re-run this script.

Run examples/01-detect.mjs meanwhile — it needs no key.
exit=1
--- with bogus key ---
/getting-started     -> { detail: 'Invalid API key' }
/api-reference       -> { detail: 'Invalid API key' }
/assets/app.css      -> { ok: true, skipped: 'not-page-view' }

Sites are auto-created from the host on the first event, then appear in the Lightsage dashboard under Agent Analytics.
exit=0
Thinking…

Good — the SDK behaves as documented (asset filtered, invalid key returns error payload, no throw). Note trackVisit returned the raw error body rather than {ok:false, error}. Worth noting in README.

Let me check LightsagePixelOptions to confirm my example 04 options are valid.

cd /sandbox/repo/node_modules/@lightsage/agent-tracker/dist && sed -n '95,135p' browser.d.ts
skipped?: "non-agent-attribution";
    skipped?: "non-agent-attribution";
    queued?: boolean;
    error?: string;
}
interface LightsagePixelOptions extends ConversionRequestContextOptions {
    /** Attach data-lightsage-conversion click listener. Defaults to true. */
    autoCaptureConversions?: boolean;
    /**
     * Record page views for agent-attributed visitors on load and on client-side
     * navigation, so conversions show the journey that led to them. Defaults to
     * true; only runs when an apiKey or pageViewEndpoint is configured.
     */
    autoCapturePageViews?: boolean;
}
/** @deprecated Use LightsagePixelOptions. */
type SapientPixelOptions = LightsagePixelOptions;
interface LightsageBrowserApi {
    init: (options?: LightsagePixelOptions) => Promise<LightsageVisitorIdentity>;
    identify: (userId: string, options?: VisitorIdentityOptions) => Promise<LightsageVisitorIdentity>;
    reset: (options?: VisitorIdentityOptions) => void;
    getVisitorIdentity: (options?: VisitorIdentityOptions) => Promise<LightsageVisitorIdentity>;
    trackConversion: (eventName: string, properties: ConversionProperties | undefined, options: TrackConversionOptions) => Promise<TrackConversionResult>;
    trackPageView: (options?: ConversionRequestContextOptions) => Promise<TrackPageViewResult>;
}
/** @deprecated Use LightsageBrowserApi. */
type SapientBrowserApi = LightsageBrowserApi;
declare global {
    interface Window {
        lightsage?: LightsageBrowserApi;
        /** @deprecated Use window.lightsage. */
        sapient?: LightsageBrowserApi;
    }
}
/**
 * Compute a first-party Lightsage browser visitor identity and persist it for
 * server middleware to attach to future tracking events.
 */
declare function getVisitorIdentity(options?: VisitorIdentityOptions): Promise<LightsageVisitorIdentity>;
/**
 * Fire-and-forget initializer for apps that only need cookies populated.
 */
Thinking…

Example 04 options are valid (LightsagePixelOptions extends ConversionRequestContextOptions extends VisitorIdentityOptions). Good.

Now update package.json with scripts and type module, and write README.md with pricing + manageability assessment.

package.json currently from npm init -y. Let me rewrite it.

File edits

1 file

Completed
Writtenpackage.jsonView changes

/sandbox/repo/package.json

Written content · previous contents were not recorded

{
"name": "lightsage-example",
"version": "1.0.0",
"private": true,
"type": "module",
"description": "Minimal Lightsage examples: agent visitor classification, visit tracking, middleware and browser conversions.",
"scripts": {
"detect": "node examples/01-detect.mjs",
"track": "node examples/02-track-visit.mjs"
},
"dependencies": {
"@lightsage/agent-tracker": "^0.1.19"
}
}
cd /tmp && timeout 30 curl -sSL https://lightsage.com/pricing 2>&1 | python3 -c "
import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); t=html.unescape(re.sub(r'\s+',' ',t));
i=t.find('Billing and services'); print(t[i:i+1500])"
Billing and services Credit top-ups — • $100 for 1,600 credits • $200 for 3,200 credits • Never expire • $100 for 1,600 credits • $200 for 3,200 credits • Never
Billing and services Credit top-ups — • $100 for 1,600 credits • $200 for 3,200 credits • Never expire • $100 for 1,600 credits • $200 for 3,200 credits • Never expire Trial — • 7 days, no card • 1,000 credits • Unrestricted access Custom Features Agent Discoverability ✓ ChatGPT, weekly ✓ 20 competitors ✓ Unlimited Agent Usability — ✓ ✓ Agent Analytics — ✓ 180 days · Conversion tracking ✓ Unlimited · Conversion tracking Agent Readiness — ✓ Up to 2,500 pages ✓ Unlimited pages Opportunities & Actions ✓ Top tasks from your trial ✓ ✓ Content generation — ✓ Up to 3 per week ✓ Custom Workflows (Premium) — ✓ ✓ MCP Analytics (Premium) — ✓ ✓ Agent Skills Evals (Premium) — ✓ ✓ Automated Reports — Weekly + exports Custom Support Email Email + Slack Custom Managed Services (optional) — +$2,000/month Custom Feature Free $0 /month Start free Scale Recommended $1,000 /month 19,000 monthly credits Start 7-day trial Enterprise Custom annual Contact sales Plans and usage Monthly price $0 $1,000/month Custom Annual agreement Credits None included 19,000 $0.0526 per credit Custom Estimated monthly capacity 10 prompts weekly ChatGPT only ~1,055 prompts tracked daily on 3 answer engines or ~158 prompts tracked daily on 1 coding agent or ~1,900 agent evals a month Custom Coding agents — Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code Claude Code OpenAI Codex Cursor GitHub Copilot Antigravity OpenClaw OpenCode Hermes Pi Kilo Grok Muse Code A
Thinking…

Note: Agent Analytics (the SDK I used) is included in all plans including Free — and it isn't credit-metered in the comparison (credits meter prompt tracking / evals / content). Worth stating: the agent-tracker SDK usage itself is included; credits are consumed by prompt tracking on answer engines, coding-agent evals, content generation. Agent Analytics on Free = "-"? Let me re-read: "Agent Analytics — ✓ 180 days · Conversion tracking ✓ Unlimited · Conversion tracking" — the columns are Free/Scale/Enterprise: Free = "—", Scale = "✓ 180 days · Conversion tracking", Enterprise = "✓ Unlimited · Conversion tracking". Hmm the "—" appears before ✓. Order in that segment: "Agent Usability — ✓ ✓" → Free "—", Scale ✓, Enterprise ✓. Then "Agent Analytics — ✓ 180 days · Conversion tracking ✓ Unlimited · Conversion tracking" → Free "—", Scale "✓ 180 days · Conversion tracking", Enterprise "✓ Unlimited".

So Agent Analytics (the tracking SDK) requires the Scale plan ($1,000/mo) or Enterprise. That's an important pricing fact: the SDK part isn't free. But the API key/track endpoints are "Included" in all plans under "API / MCP / CLI access" — that row refers to API access generally. Ambiguous. I should state it carefully: per the comparison table, Agent Analytics is a paid feature (Scale+); the no-auth detect endpoint is free.

Let me verify by checking if there's a docs page on pricing/credits. Quick check for a docs site: lightsage.com has "Resources". Let's not over-invest. Maybe check app.lightsage.com/docs or docs path.

for u in https://lightsage.com/docs https://app.lightsage.com/docs https://lightsage.com/developers https://lightsage.com/api-docs; do echo -n "$u -> "; timeout 15 curl -sSL -o /dev/null -w "%{http_code} %{url_effective}\n" "$u"; done
https://lightsage.com/docs -> 200 https://lightsage.com/docs
https://lightsage.com/docs -> 200 https://lightsage.com/docs
https://app.lightsage.com/docs -> 200 https://app.lightsage.com/welcome?redirect_url=https%3A%2F%2Fapp.lightsage.com%2Fdocs%3F_org_auth_retry%3D1
https://lightsage.com/developers -> 404 https://lightsage.com/developers
https://lightsage.com/api-docs -> 404 https://lightsage.com/api-docs
cd /tmp && timeout 60 npm view @lightsage/mcp --json 2>&1 | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('description')); print(d.get('homepage')); print(d.get('bin'))"; timeout 30 curl -sS https://registry.npmjs.org/@lightsage/mcp/latest | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['readme'][:2500])"
FailedModel Context Protocol (MCP) server for the Lightsage Public API.
Model Context Protocol (MCP) server for the Lightsage Public API.
https://github.com/lightsagehq/mcp#readme
{'mcp': 'bin/mcp-server.js'}
Traceback (most recent call last):
  File "<string>", line 1, in <module>
KeyError: 'readme'


Command exited with code 1
Thinking…

Let me check lightsage.com/docs content briefly — maybe public docs with pricing/credits info. And the MCP readme from the tarball quickly (it's a developer workflow an agent can act on: MCP server for Lightsage Public API — requires API key).

Let me be quick.

timeout 30 curl -sSL https://lightsage.com/docs | python3 -c "
import sys,html,re; t=sys.stdin.read(); t=re.sub(r'<script.*?</script>','',t,flags=re.S); t=re.sub(r'<style.*?</style>','',t,flags=re.S); t=re.sub(r'<[^>]+>',' ',t); print(html.unescape(re.sub(r'\s+',' ',t))[:2500])"
Quickstart - Lightsage Documentation Index Fetch the complete documentation index at: /docs/llms.txt Use this file to discover all available pages before explo
 Quickstart - Lightsage Documentation Index Fetch the complete documentation index at: /docs/llms.txt Use this file to discover all available pages before exploring further. Skip to main content Lightsage home page Search... ⌘ K Homepage Dashboard Dashboard Search... Navigation Quickstart Quickstart Documentation CLI Reference MCP Reference API Reference Changelog Quickstart Quickstart Migrate from Sapient Onboard your AI agent Agent Skills Lightsage MCP Lightsage CLI Workflows Docs for agents Guides Pull actionables into your agent Running evals Agent Analytics Attributed growth Integrations Google Cloud DNS Lightsage Docs Agent Overview GitHub PR evals On this page Back to top Before you start Create an API key Choose how to connect Fetch your first work item Next step Quickstart Quickstart Copy page Copy page Set up Lightsage access, choose a client, and pull your first agent-ready work item. Copy page Copy page Use this quickstart to get from a Lightsage account to your first agent-ready task. You will create an API key, choose how your agent connects to Lightsage, and confirm that you can fetch work with execution prompts. ​ Before you start You need: A Lightsage organization with Visibility or API Performance data. Access to Settings > API Keys in the Lightsage dashboard. A terminal for the Lightsage CLI, or an agent that supports Lightsage MCP. ​ Create an API key Open Settings > API Keys . Click Create API key . Copy the key when it appears. Store it in your shell or secret manager. export LIGHTSAGE_API_KEY = "ls_cli_your_api_key" API keys are shown once. Do not paste them into prompts or commit them to your repository. ​ Choose how to connect Use the CLI when you want repeatable terminal commands or CI-friendly exports. Use MCP when you want an agent to fetch Lightsage context directly during a working session. CLI Reference Install and authenticate the Lightsage CLI. Lightsage MCP Connect Lightsage to supported coding agents. ​ Fetch your first work item Start with active daily tasks. Each task includes prompt context and a paste-ready execution_prompt for an agent. curl "https://api.lightsage.com/v1/tasks?status=active" \ -H "X-Lightsage-Api-Key: $LIGHTSAGE_API_KEY " You can also list prompt opportunities directly: curl "https://api.lightsage.com/v1/opportunities?limit=10" \ -H "X-Lightsage-Api-Key: $LIGHTSAGE_API_KEY " ​ Next step Use the workflow guide to hand those tasks or opportunities to a coding agent and schedule the resulting work. Pull
Thinking…

There's a real public docs site with llms.txt, a CLI, MCP, and a Public API at api.lightsage.com/v1 requiring ls_cli_ key. This is the primary developer workflow (different from the tracking SDK).

Quickstart requires an API key from the dashboard — I don't have credentials. That's a blocker for the full quickstart, but not for what I built (detect endpoint is public, SDK is installed and exercised).

Let me check /docs/llms.txt and the API reference to make my example accurate, and check if there's a CLI npm package (@lightsage/cli?).

Let me fetch llms.txt.

timeout 30 curl -sSL https://lightsage.com/docs/llms.txt | head -60; echo "=== npm cli ==="; timeout 40 npm search @lightsage 2>&1 | head -20
# Lightsage
# Lightsage

- [Quickstart](https://lightsage.com/docs/index.md): Set up Lightsage access, choose a client, and pull your first agent-ready work item.
- [Migrate from Sapient to Lightsage](https://lightsage.com/docs/migration-guide.md): Update Sapient CLI, MCP, Public API, Agent Tracker, and Docs Agent integrations to their Lightsage names.
- [Onboard your AI agent to Lightsage](https://lightsage.com/docs/agent-onboarding.md): Give your coding agent Lightsage skills, connected tools, workflows, and current documentation.
- [Agent Skills](https://lightsage.com/docs/agent-onboarding/agent-skills.md): Install three Lightsage Agent Skills in a compatible coding agent.
- [Lightsage MCP](https://lightsage.com/docs/agent-onboarding/lightsage-mcp.md): Connect a compatible AI client to the hosted Lightsage MCP server.
- [Lightsage CLI](https://lightsage.com/docs/agent-onboarding/lightsage-cli.md): Install the Lightsage CLI, authenticate your coding agent, and verify access.
- [Workflows](https://lightsage.com/docs/agent-onboarding/workflows.md): Use repeatable prompts that help your coding agent act on Lightsage findings.
- [Docs for agents](https://lightsage.com/docs/agent-onboarding/docs-for-agents.md): Give your AI agent current Lightsage documentation in a machine-readable format.
- [Pull actionables into your agent](https://lightsage.com/docs/workflows/pull-actionables-into-your-agent.md): Copy a prompt that has your coding agent pull your Lightsage tasks and opportunities and ship fixes for them.
- [Running evals](https://lightsage.com/docs/guides/running-evals.md): Create, fan out, monitor, and inspect coding-agent evals with the Lightsage CLI.
- [Measure agent-attributed growth](https://lightsage.com/docs/agent-analytics/attributed-growth.md): Connect visits from recognized AI agent sources to sign-up, demo, checkout, and activation milestones in Agent Analytics.
- [Google Cloud DNS integration](https://lightsage.com/docs/agent-analytics/integrations/google-cloud-dns.md): Set up Lightsage Agent Analytics for a domain managed in Google Cloud DNS by routing traffic through a Google load balancer and exporting request logs.
- [Lightsage Docs Agent](https://lightsage.com/docs/lightsage-docs-agent/overview.md): Run Lightsage docs evals from GitHub pull requests before documentation changes ship.
- [Run docs evals from GitHub](https://lightsage.com/docs/lightsage-docs-agent/github-pr-evals.md): Use Lightsage slash commands on GitHub pull requests to run docs evals against staging documentation.
- [CLI Reference](https://lightsage.com/docs/cli-reference/index.md): Install, authenticate, and navigate the Lightsage CLI.
- [Visibility](https://lightsage.com/docs/cli-reference/visibility.md): Retrieve visibility metrics, citations, competitors, and prompt reports from the Lightsage CLI.
- [Opportunities](https://lightsage.com/docs/cli-reference/opportunities.md): List and retrieve ranked visibility opportunities from the Lightsage CLI.
- [Tasks](https://lightsage.com/docs/cli-reference/tasks.md): Create, list, retrieve, and update daily tasks from the Lightsage CLI.
- [Prompts](https://lightsage.com/docs/cli-reference/prompts.md): Create, list, retrieve, update, and delete Visibility prompts from the Lightsage CLI.
- [Prompt topics](https://lightsage.com/docs/cli-reference/prompt-topics.md): List and manage Visibility prompt topics from the Lightsage CLI.
- [Prompt runs](https://lightsage.com/docs/cli-reference/prompt-runs.md): List and retrieve stored prompt runs from the Lightsage CLI.
- [Content](https://lightsage.com/docs/cli-reference/content.md): Generate drafts, finalize articles, and retrieve content from the Lightsage CLI.
- [Site audits](https://lightsage.com/docs/cli-reference/site-audit.md): Retrieve the latest website audit and inspect individual audited pages from the Lightsage CLI.
- [Evals](https://lightsage.com/docs/cli-reference/evals.md): Create and manage reusable coding-agent simulations from the Lightsage CLI.
- [Eval runs](https://lightsage.com/docs/cli-reference/eval-runs.md): Start saved or direct eval runs and inspect their lifecycle state from the Lightsage CLI.
- [Eval results](https://lightsage.com/docs/cli-reference/eval-results.md): List and retrieve completed eval results from the Lightsage CLI.
- [Agents](https://lightsage.com/docs/cli-reference/agents.md): List available coding agents for eval runs from the Lightsage CLI.
- [Repositories](https://lightsage.com/docs/cli-reference/repositories.md): Manage Git repository workspaces for eval configurations from the Lightsage CLI.
- [Environments](https://lightsage.com/docs/cli-reference/environments.md): List reusable runtime environments from the Lightsage CLI.
- [MCP servers](https://lightsage.com/docs/cli-reference/mcp-servers.md): List reusable MCP server integrations from the Lightsage CLI.
- [CLIs](https://lightsage.com/docs/cli-reference/clis.md): List reusable CLI tools from the Lightsage CLI.
- [Skills](https://lightsage.com/docs/cli-reference/skills.md): List reusable skills available to workflows and eval configurations from the Lightsage CLI.
- [Lightsage MCP](https://lightsage.com/docs/mcp-reference/index.md): Choose a client and connect Lightsage MCP to coding agents and MCP-capable tools.
- [Install Lightsage MCP in ChatGPT](https://lightsage.com/docs/mcp-reference/chatgpt.md): Connect ChatGPT developer mode to Lightsage MCP with a preconfigured public OAuth client.
- [Install Lightsage MCP in Claude](https://lightsage.com/docs/mcp-reference/claude.md): Connect Claude hosted connector surfaces to Lightsage MCP with OAuth.
- [Install Lightsage MCP in Claude Code](https://lightsage.com/docs/mcp-reference/claude-code.md): Connect Claude Code to Lightsage MCP with a remote HTTP server and the X-Lightsage-Api-Key header.
- [Install Lightsage MCP in Codex](https://lightsage.com/docs/mcp-reference/codex.md): Connect Codex to Lightsage MCP with a streamable HTTP server in config.toml.
- [Install Lightsage MCP in Cursor](https://lightsage.com/docs/mcp-reference/cursor.md): Connect Cursor to Lightsage MCP with OAuth or an API key.
- [Install Lightsage MCP in Gemini CLI](https://lightsage.com/docs/mcp-reference/gemini-cli.md): Connect Gemini CLI to Lightsage MCP with a streamable HTTP MCP server.
- [Install Lightsage MCP in GitHub Copilot](https://lightsage.com/docs/mcp-reference/github-copilot.md): Connect GitHub Copilot to Lightsage MCP from VS Code or Copilot CLI.
- [Install Lightsage MCP in Kilo](https://lightsage.com/docs/mcp-reference/kilo.md): Connect Kilo to Lightsage MCP with a remote MCP server entry.
- [Install Lightsage MCP in OpenCode](https://lightsage.com/docs/mcp-reference/opencode.md): Connect OpenCode to Lightsage MCP with a remote MCP server entry.
- [API Reference](https://lightsage.com/docs/api-reference/introduction.md): Authenticate and call the Lightsage public API from your own integrations.
- [Get Status](https://lightsage.com/docs/api-reference/status/get-status.md): Checks API liveness without authentication. Use `GET /v2/auth/status` to validate an API key.
- [Get Auth Status](https://lightsage.com/docs/api-reference/status/get-auth-status.md): Validates the API key and returns its organization ID, API key ID, scopes, and authentication status.
- [Retrieve Current Visibility](https://lightsage.com/docs/api-reference/visibility/retrieve-current-visibility.md): Returns recommendation score, visibility score, ranks, and date-range metadata for the configured primary company, with the same metrics for the previous period, run coverage, and per-run rates. Metrics are computed from active prompts only; when no data exists, values are `null`. Pass `period` (for…
- [Retrieve Visibility Overview](https://lightsage.com/docs/api-reference/visibility/retrieve-visibility-overview.md): Returns aggregate snapshot, daily trends, per-platform breakdowns, and competitor comparison, with run coverage and per-run rates against the previous period. Calculated from active prompts only.
- [List Top Cited Urls](https://lightsage.com/docs/api-reference/visibility/list-top-cited-urls.md): Returns URL-level citation counts ranked from most to least cited. Results can be filtered by platform and to owned or external sources. For totals by URL prefix, domain, or day compared with the previous period, use the citations summary.
- [Retrieve Prompt Visibility Report](https://lightsage.com/docs/api-reference/visibility/retrieve-prompt-visibility-report.md): Returns per-topic and per-prompt visibility metrics including recommendation score, rank, visibility score, average position, and citation counts.
- [Retrieve Citation Summary](https://lightsage.com/docs/api-reference/visibility/retrieve-citation-summary.md): Returns citation totals grouped by URL prefix (domain and first path segment, such as `example.com/blog`), URL, domain, or day, each compared with the previous period, with per-run rates and run coverage. Filter to one section of a site with `url_prefix`, for example `https://example.com/blog/`. Agg…
- [List Prompts](https://lightsage.com/docs/api-reference/prompts/list-prompts.md): Returns active and inactive prompts by default, newest first. Set `include_archived` to include archived prompts. Without `limit`, every matching prompt is returned in one response. Set `limit` to page through them: `count` is the total across every page, so request the next page with `offset` until…
- [Create Prompts](https://lightsage.com/docs/api-reference/prompts/create-prompts.md): Creates prompts in a batch. Each item is validated independently, so the response can contain both created prompts and individual errors.
- [List Topics](https://lightsage.com/docs/api-reference/prompts/list-topics.md): Returns all prompt topics available to the organization.
- [Create Topic](https://lightsage.com/docs/api-reference/prompts/create-topic.md): Creates a prompt topic and returns the saved topic.
- [Delete Topic](https://lightsage.com/docs/api-reference/prompts/delete-topic.md): Archives a prompt topic. Set `archive_prompts` to archive remaining prompts; otherwise the request fails with `400` if prompts remain.
- [Update Topic](https://lightsage.com/docs/api-reference/prompts/update-topic.md): Renames a prompt topic and returns the updated topic.
- [List Targets](https://lightsage.com/docs/api-reference/prompts/list-targets.md): Returns the answer engines and coding agents that run the organization's prompts on schedule, with each target's topic and frequency. Provide `topic_id` to list one topic's targets.
- [Set Targets](https://lightsage.com/docs/api-reference/prompts/set-targets.md): Sets which answer engines and coding agents run a topic's prompts on schedule, the same way the prompt editor does. The list replaces the topic's targets: targets you leave out stop running. Every prompt in the topic is set to exactly these platforms; you can still narrow one prompt afterwards with…
curl: (23) Failure writing output to destination
=== npm cli ===
@lightsage/mcp
Model Context Protocol (MCP) server for the Lightsage Public API.
Version 0.4.24 published 2026-11-09 by GitHub Actions
Maintainers: seanflyyy
https://npm.im/@lightsage/mcp

@lightsage/agent-tracker
Track AI agent visits, and revenue attribution for your site. Works with Next.js, Vercel, Cloudflare, Express, and Netlify DNS proxy setups.
Version 0.1.19 published 2026-10-18 by GitHub Actions
Maintainers: seanflyyy
Keywords: ai analytics agent middleware nextjs vercel netlify cloudflare express lightsage
https://npm.im/@lightsage/agent-tracker

@lightsage/mcp-tracker
Track MCP tool calls from AI agents. Works with @modelcontextprotocol/sdk and custom servers.
Version 0.2.3 published 2026-10-19 by GitHub Actions
Maintainers: seanflyyy
Keywords: mcp model-context-protocol ai analytics agent tool-tracking lightsage
https://npm.im/@lightsage/mcp-tracker

Thinking…

Great — there's a full public API (api.lightsage.com/v1, header X-Lightsage-Api-Key, key prefix ls_cli_) plus an unauthenticated status endpoint. Let me test GET /v1/status (no auth) to add a second no-key example: check API liveness. That's from the public API, distinct from ls.lightsage.com.

Let me fetch the API reference introduction and status docs quickly, then test.

timeout 30 curl -sSL https://lightsage.com/docs/api-reference/introduction.md | head -80; echo "=== status ==="; timeout 20 curl -sS https://api.lightsage.com/v1/status -w "\nHTTP %{http_code}\n"
> ## Documentation Index
> ## Documentation Index
> Fetch the complete documentation index at: https://lightsage.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Authenticate and call the Lightsage public API from your own integrations.

The Lightsage public API exposes the same contract used by the CLI. Use it to manage Visibility prompts, start eval runs, and work with API Performance resources from your own services and automation.

> [!NOTE]
>
> The public API uses API key authentication. It does not require browser login.

## Base URL

All requests use this production base URL:

```text theme={null}
https://api.lightsage.com
```

> [!WARNING]
>
> Use `https://api.lightsage.com` for API calls.

## Authenticate

Create an API key in the Lightsage dashboard:

1. Open **Settings > API Keys**.
2. Click **Create API key**.
3. Copy the key when it is shown.

Send the key on every authenticated request with the `X-Lightsage-Api-Key` header:

```bash theme={null}
curl https://api.lightsage.com/v1/auth/status \
  -H "X-Lightsage-Api-Key: ls_cli_your_api_key"
```

You can also store the key with the CLI and reuse the same credential for local testing:

```bash theme={null}
lightsage auth login
```

## Check API health

Verify that the public API is reachable:

```bash theme={null}
curl https://api.lightsage.com/v1/status
```

Check that your API key is valid:

```bash theme={null}
curl https://api.lightsage.com/v1/auth/status \
  -H "X-Lightsage-Api-Key: ls_cli_your_api_key"
```

## Explore endpoints

The sidebar groups endpoints by resource:

* **Status** — health and authentication checks
* **Prompts** — Visibility prompt and topic workflows
* **API Performance** — eval runs, sources, targets, skills, and related resources

Use the interactive playground on each endpoint page to send requests with your API key.

## Create an eval simulation

`POST /v2/evals` stores a reusable simulation matrix. Prompts, judges, agents,
CLIs, and MCP servers are arrays. `persona` applies to every attempt, and `runs`
controls how many times each prompt-agent pair executes.

```bash theme={null}
curl https://api.lightsage.com/v2/evals \
=== status ===
{"status":"ok"}
HTTP 200
Thinking…

Excellent. GET /v1/status works with no auth. Add example 05: public API — status (no key) + auth/status + list tasks/opportunities (with key, graceful).

Let me add examples/05-public-api.mjs.

File edits

1 file

Completed
Writtenexamples/05-public-api.mjsView changes

/sandbox/repo/examples/05-public-api.mjs

Written content · previous contents were not recorded

#!/usr/bin/env node
/**
* Lightsage example 5 — the Lightsage Public API (the CLI's contract).
*
* Base URL: https://api.lightsage.com
* Auth: X-Lightsage-Api-Key header, keys look like `ls_cli_...`
* (created in the dashboard under Settings > API Keys, shown once)
*
* GET /v1/status -> liveness, no auth required (always runs here)
* GET /v1/auth/status -> validates your key, returns org id + scopes
* GET /v1/tasks -> daily agent-ready work items, each with an
* execution_prompt you can hand to a coding agent
*
* Run:
* node examples/05-public-api.mjs # health check only
* LIGHTSAGE_API_KEY=ls_cli_... node examples/05-public-api.mjs
*/
const BASE_URL = "https://api.lightsage.com";
const apiKey = process.env.LIGHTSAGE_API_KEY;
async function get(path) {
const headers = apiKey ? { "X-Lightsage-Api-Key": apiKey } : {};
const res = await fetch(`${BASE_URL}${path}`, { headers });
const text = await res.text();
let body;
try {
body = JSON.parse(text);
} catch {
body = text;
}
return { status: res.status, body };
}
// 1. Liveness — no credentials needed.
console.log("GET /v1/status ->", await get("/v1/status"));
if (!apiKey) {
console.log(
[
"",
"No LIGHTSAGE_API_KEY set, stopping after the health check.",
"Create one in the Lightsage dashboard (Settings > API Keys), then:",
" export LIGHTSAGE_API_KEY=ls_cli_...",
"",
"The key is shown once and gates everything else: prompts, visibility",
"metrics, eval runs, tasks and opportunities. Agent Analytics uses a",
"separate `ls_track_...` key (see examples/02-track-visit.mjs).",
].join("\n"),
);
process.exit(0);
}
// 2. Who am I?
console.log("\nGET /v1/auth/status ->", await get("/v1/auth/status"));
// 3. Work items an agent can act on right now.
const tasks = await get("/v1/tasks?status=active");
console.log("\nGET /v1/tasks?status=active -> HTTP", tasks.status);
const items = Array.isArray(tasks.body) ? tasks.body : (tasks.body?.tasks ?? tasks.body?.items ?? []);
console.log(`active tasks: ${items.length}`);
for (const task of items.slice(0, 5)) {
console.log(` - [${task.id}] ${task.title ?? task.prompt ?? JSON.stringify(task).slice(0, 90)}`);
}
// 4. Ranked visibility opportunities.
const opps = await get("/v1/opportunities?limit=5");
console.log("\nGET /v1/opportunities?limit=5 -> HTTP", opps.status);
console.log(JSON.stringify(opps.body).slice(0, 600));
cd /sandbox/repo && node examples/05-public-api.mjs; echo "exit=$?"
GET /v1/status -> { status: 200, body: { status: 'ok' } }
GET /v1/status -> { status: 200, body: { status: 'ok' } }

No LIGHTSAGE_API_KEY set, stopping after the health check.
Create one in the Lightsage dashboard (Settings > API Keys), then:
  export LIGHTSAGE_API_KEY=ls_cli_...

The key is shown once and gates everything else: prompts, visibility
metrics, eval runs, tasks and opportunities. Agent Analytics uses a
separate `ls_track_...` key (see examples/02-track-visit.mjs).
exit=0
Thinking…

Both keyless examples run live. Now add package.json script for api, and write the README with:

  • what was built & how to run
  • verified output
  • pricing
  • manageability assessment
  • blocker: full quickstart needs dashboard-issued API key (no signup possible from here)

Update package.json scripts.

File edits

1 file

Completed
Modifiedpackage.json+2−1View changes

/sandbox/repo/package.json

Replacement excerpt

"detect": "node examples/01-detect.mjs",
- "track": "node examples/02-track-visit.mjs"
+ "track": "node examples/02-track-visit.mjs",
+ "api": "node examples/05-public-api.mjs"
Thinking…

Let me check the Agent Analytics docs page for pricing/credits specifics about tracking to be accurate about whether tracking consumes credits. Fetch attributed-growth.md quickly and grep for credit/pricing. Also check pricing docs page for credits consumption table.

timeout 30 curl -sSL https://lightsage.com/docs/llms.txt | grep -i "pricing\|credit\|billing\|plan" | head -10; echo "=== attributed growth ==="; timeout 30 curl -sSL https://lightsage.com/docs/agent-analytics/attributed-growth.md | head -60
- [List Agents](https://lightsage.com/docs/api-reference/agents/list-agents.md): Returns the coding-agent harness-and-model combinations your plan can run, as s
- [List Agents](https://lightsage.com/docs/api-reference/agents/list-agents.md): Returns the coding-agent harness-and-model combinations your plan can run, as single agent IDs. Pass them as `agent` to `POST /v2/prompts/runs` to see how coding agents answer your prompts, with mentions and citations, or to `PUT /v2/prompts/targets` to run them on a schedule; to `POST /v2/agent-pre…
- [How Lightsage credits work and how to estimate usage](https://lightsage.com/docs/account/credits.md): Learn how Lightsage credits work, what each platform type costs per run, and how to estimate your monthly usage before choosing a plan.
- [Frequently asked questions about Lightsage](https://lightsage.com/docs/account/faq.md): Answers to common questions about Lightsage's AEO platform, supported AI platforms, credit system, billing, and how to get started tracking your API.
- [Lightsage pricing: Free, Pro, and Enterprise plans](https://lightsage.com/docs/account/plans.md): Compare Free, Pro, and Enterprise plans to find the right fit for tracking and improving your API's visibility across AI coding agents.
- [Key concepts: Lightsage terminology and metrics explained](https://lightsage.com/docs/key-concepts.md): Definitions for every term you'll encounter in Lightsage — from GEO and AEO to Visibility Score, API Performance, credits, llms.txt, and MCP.
=== attributed growth ===
> ## Documentation Index
> Fetch the complete documentation index at: https://lightsage.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Measure agent-attributed growth

> Connect visits from recognized AI agent sources to sign-up, demo, checkout, and activation milestones in Agent Analytics.

Agent-attributed growth connects a browser visit from a recognized AI source to conversion milestones that happen on your website or app. Use it to measure outcomes such as sign-up intent, completed registration, demo requests, checkout, and API key creation.

> [!NOTE]
>
> Agent Attributed Conversions is enabled per organization. If you do not see this panel under **Agent Analytics**, ask Lightsage to enable attribution for your organization.

## What agent-attributed means

Lightsage attributes a conversion when the visitor's browser carries a recognized AI agent source and your application sends an explicit conversion event.

The source can come from:

* An external referrer such as `chatgpt.com`, `claude.ai`, or `perplexity.ai`
* A source parameter such as `utm_source=chatgpt.com`
* A stored first-touch or session touch captured earlier in the same browser

Lightsage normalizes known aliases and domains to agent names. For example, `chat.openai.com`, `chatgpt.com`, and `openai` resolve to ChatGPT. Plain GitHub referrals are excluded, while `github-copilot` remains a valid agent source.

> [!WARNING]
>
> An agent crawler request does not automatically become a human conversion. The human browser must retain an agent source through its referrer, URL parameters, or stored attribution state. Treat this as acquisition attribution, not proof that a specific crawler request caused the outcome.

## How the attribution flow works

**1. A visitor arrives from an AI source**

The browser helper resolves the current source from explicit URL parameters first, then from an external referrer. Internal referrals across your own subdomains are ignored.

**2. Lightsage creates browser identity**

Lightsage stores a first-party visitor ID and a rolling session ID. By default, the visitor ID lasts 365 days and the session lasts 30 minutes.

**3. Your application records milestones**

Send an `intent` event for a meaningful action such as starting sign-up. Send a `completed` event only after the outcome is confirmed.

**4. Your application identifies the user**

After authentication, attach your stable application user ID. This connects the known user to the existing anonymous browser journey.

**5. Agent Analytics builds the funnel**

Lightsage keeps conversions from recognized agent sources, deduplicates events by event ID, groups events by browser or user identity, and orders funnel steps from observed journeys. You can override the displayed order in the dashboard.

Lightsage stores three attribution touchpoints:

| Touchpoint | Meaning |
| - | - |
| **First touch** | The first valid acquisition source for the visitor. A later agent source can replace an initial `direct` touch. |
| **Session touch** | The source that began the current browser session. |
| **Current touch** | The source resolved from the current page URL and referrer. |

Thinking…

Good. Fetch credits.md for concrete pricing detail (cost per run) — useful for the pricing answer.

timeout 30 curl -sSL https://lightsage.com/docs/account/credits.md | head -90
> ## Documentation Index
> ## Documentation Index
> Fetch the complete documentation index at: https://lightsage.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How Lightsage credits work and how to estimate usage

> Learn how Lightsage credits work, what each platform type costs per run, and how to estimate your monthly usage before choosing a plan.

Credits are the billing unit Lightsage uses for running AI evaluations. Every time Lightsage runs a prompt on an AI platform or tests an API endpoint with a coding agent, it consumes credits. The number of credits a run costs depends on the type of platform — lighter answer engine lookups cost less than full coding agent executions that write and run real code.

## Credit costs by platform type

Different platform types cost different amounts because they require different levels of compute and time to run.

| Platform type | Examples | Credits per run |
| - | - | - |
| **AI Search / Answer Engine** | ChatGPT, Perplexity, Google AI Overviews, Gemini Search, Claude, Google AI Mode, Microsoft Copilot | 1 credit |
| **Coding Agent** | Claude Code, OpenAI Codex, Cursor, GitHub Copilot, Gemini CLI, OpenClaw, OpenCode, Hermes, Pi, Kilo | 5 credits |
| **Model (API Performance)** | DeepSeek, Kimi, Z.ai, Grok | 1 credit |

> [!NOTE]
>
> Coding agents cost 5 credits per run because they execute real code, navigate documentation, make tool calls, and recover from errors — a significantly more compute-intensive process than querying an answer engine for a text response.

## Why coding agents cost more

When Lightsage runs a prompt through an answer engine, it submits a query and captures the text response. The entire process takes seconds and uses minimal compute.

Running the same prompt through a coding agent is a fundamentally different operation. The agent reads your documentation, decides which SDK or endpoint to call, writes code, executes it, handles errors, and may retry multiple times. Lightsage captures each step of this process — recommendation rate, tool call success rate, error recovery, and completion. That depth of evaluation takes more time and more compute, which is reflected in the 5-credit cost per run.

## Credits per plan

> [card] Free plan
>
> **250 starter credits** on signup. No credit card required. Use them to explore your visibility score, run prompts on one AI platform, and check your Devtool Arena ranking.

> [card] Pro plan
>
> **1,500 credits per month**, included with the \$100/month subscription. Credits reset each billing cycle. Top-ups are available if you need more capacity mid-cycle.

> [!NOTE]
>
> Credits do not expire within your billing period. Any unused credits at the end of the cycle do not roll over to the next month.

## Estimating your monthly usage

Use this formula to estimate how many credits you'll need each month:

```
Monthly credits = platforms × prompts × runs per month
```

Break it down by what you're tracking:

**Prompt Tracking (answer engines and coding agents)**

```
credits = number of platforms × number of prompts × monthly run frequency
```

**API Performance (coding agents and models)**

```
credits = (agents × endpoints × run frequency) + (models × endpoints × run frequency)
```

### Example calculations

> [accordion] AI search tracking: 20 prompts across 2 platforms
>
> Tracking 20 prompts on 2 AI Search platforms, running daily (30×/month):
>
> ```
> 20 prompts × 2 platforms × 30 runs = 1,200 credits/month
> ```
>
> At 1 credit per run, this is well within the Pro plan's 1,500 monthly credits, leaving 300 credits for API Performance evaluations.

> [accordion] Coding agent evaluations: 5 endpoints across 3 agents
>
> Testing 5 API endpoints with 3 coding agents, running daily (30×/month):
>
> ```
> 5 endpoints × 3 agents × 30 runs × 5 credits = 2,250 credits/month
> ```
>
> This exceeds the Pro plan's 1,500 monthly credits. To stay within budget, reduce to alternate-day scheduling (15×/month) or narrow to 2 agents:
>
> ```
> 5 endpoints × 2 agents × 30 runs × 5 credits = 1,500 credits/month
Thinking…

Note discrepancy: docs say Free = 250 starter credits, Pro = $100/mo with 1,500 credits; the marketing pricing page says Free = 10 prompts weekly no credits, Scale = $1,000/mo with 19,000 credits ($0.0526/credit), Enterprise custom. Docs appear older (Pro $100). I'll report the live pricing page as authoritative and flag the doc drift — that's an honest, useful finding.

Now write README.md.

File edits

1 file

Completed
WrittenREADME.mdView changes

/sandbox/repo/README.md

Written content · previous contents were not recorded

# Lightsage — minimal working example
Small, runnable examples against the **hosted** Lightsage product. No local
stacks, no containers, no long-running processes. Everything talks to
Lightsage's public endpoints over HTTPS.
## What Lightsage is
An "Agent-Led Growth" platform for software companies. Two developer-facing
surfaces, with two different API keys:
| Surface | Base URL | Auth | What it does |
| --- | --- | --- | --- |
| Public API (+ CLI + MCP) | `https://api.lightsage.com` | `X-Lightsage-Api-Key: ls_cli_...` | Visibility prompts, prompt runs, evals, tasks, opportunities |
| Agent Analytics SDK | `https://ls.lightsage.com/api/v1/agent-tracking/*` | `x-api-key: ls_track_...` | Detects AI coding agents hitting your docs, tracks visits + attributed conversions |
Docs index for machines: <https://lightsage.com/docs/llms.txt>
OpenAPI spec (870 paths, the dashboard's internal API):
<https://ls.lightsage.com/api/v1/openapi.json>
## Install
```bash
npm install # installs @lightsage/agent-tracker (zero runtime deps)
```
## Run
| Command | Needs a key? | What it proves |
| --- | --- | --- |
| `npm run detect` | **No** | Live visitor classification from Lightsage's detection API |
| `npm run api` | No (health check only) | Public API liveness; with a key also auth/status, tasks, opportunities |
| `npm run track` | Yes (`ls_track_...`) | Records real visits via the SDK `trackVisit()` |
### Verified output — `npm run detect` (no credentials)
```
┌─────────┬──────────────────────────────────────────┬──────────────────┬──────────────────────────┬──────────┐
│ (index) │ visitor │ category │ agent │ page_view│
├─────────┼──────────────────────────────────────────┼──────────────────┼──────────────────────────┼──────────┤
│ 0 │ 'Claude Code reading your docs' │ 'coding-agent' │ 'unknown-coding-agent' │ true │
│ 1 │ 'OpenAI Codex CLI' │ 'coding-agent' │ 'codex' │ true │
│ 2 │ 'Generic command-line HTTP client' │ 'bot' │ 'curl/' │ false │
│ 3 │ 'ChatGPT-User (browsing agent)' │ 'browsing-agent' │ 'chatgpt' │ true │
│ 4 │ 'Googlebot (search crawler, not an agent)│ 'bot' │ 'googlebot' │ true │
└─────────┴──────────────────────────────────────────┴──────────────────┴──────────────────────────┴──────────┘
```
`GET /api/v1/agent-tracking/detect` is documented as "Classify a visitor
without recording. Useful for testing. No auth required." Classification runs
server-side, so detection improves without an SDK upgrade.
### Verified output — `npm run track` with a bogus key
```
/getting-started -> { detail: 'Invalid API key' }
/api-reference -> { detail: 'Invalid API key' }
/assets/app.css -> { ok: true, skipped: 'not-page-view' } # filtered client-side, no network call
```
Worth knowing: on auth failure `trackVisit()` resolves with the raw error body
rather than the documented `{ ok: false, error }` shape. It never throws, and
times out silently at 2.5s — good for production, but check `result.ok` and
`result.detail` if you want to surface failures.
## Files
| File | Purpose |
| --- | --- |
| `examples/01-detect.mjs` | Keyless classification of 5 visitor types (runs live) |
| `examples/02-track-visit.mjs` | SDK `init()` + `trackVisit()`; exits with instructions if no key |
| `examples/03-express-middleware.mjs` | `withAgentTracking()` reference snippet (+ Next.js / Cloudflare / Netlify equivalents in comments). **Not run** — it is a web server |
| `examples/04-browser-conversions.ts` | Browser identity, `identifyUser()`, `trackConversion()`, declarative pixel |
| `examples/05-public-api.mjs` | Public API: `/v1/status` keyless, then auth/tasks/opportunities with a key |
## How pricing works
Lightsage bills in **credits**; a credit is one AI run. Cost per run depends on
the platform type:
| Platform type | Examples | Credits per run |
| --- | --- | --- |
| Answer engine / AI search | ChatGPT, Perplexity, Google AI Overviews, Gemini Search, Claude, Copilot | 1 |
| Coding agent | Claude Code, Codex, Cursor, Copilot, Gemini CLI, OpenCode, Pi, Kilo | 5 |
| Model (API Performance) | DeepSeek, Kimi, Z.ai, Grok | 1 |
Estimate: `credits = platforms × prompts × runs per month` (coding agents × 5).
Live plans (from <https://lightsage.com/pricing>):
| | Free | Scale | Enterprise |
| --- | --- | --- | --- |
| Price | $0/mo | $1,000/mo | Custom annual |
| Credits | none included | 19,000/mo (~$0.0526/credit) | Custom |
| Capacity | 10 prompts/week, ChatGPT only | ~1,055 prompts/day on 3 answer engines, or ~158/day on 1 coding agent, or ~1,900 agent evals/mo | Custom |
| Agent Analytics | — | ✓ 180 days + conversion tracking | ✓ Unlimited |
| API / MCP / CLI | ✓ | ✓ | ✓ |
| Top-ups | — | $100 → 1,600 credits, $200 → 3,200, never expire | Custom |
| Trial | — | 7 days, no card, 1,000 credits | — |
| Managed services | — | +$2,000/mo | Custom |
Notes:
- **The tracking SDK used here is not credit-metered** — credits are consumed by
prompt runs and evals, not by inbound visit events. But per the comparison
table, **Agent Analytics itself is a Scale/Enterprise feature**, so recording
visits is a paid capability even though each event costs no credits.
- The keyless endpoints used by `npm run detect` and `npm run api` are free.
- **Doc drift:** `docs/account/credits.md` and `docs/account/plans.md` still
describe a "Pro" plan at $100/mo with 1,500 credits and 250 free starter
credits. The live pricing page shows Free/$0, Scale/$1,000, Enterprise. Trust
the pricing page; the docs page is stale.
## Will this be easy for me (an agent) to manage?
**Yes, with one gate.**
Easy:
- Docs are explicitly agent-first: `llms.txt` index, `.md` version of every
page, a published OpenAPI spec, an MCP server (`@lightsage/mcp`) and a CLI.
This is about as agent-friendly a product surface as exists.
- SDK is one zero-dependency npm package; middleware integration is a single
wrapped export per platform.
- Sensible failure modes: non-blocking, silent timeouts, client-side asset
filtering, auto-created sites from the host domain.
- Server-side classification means no SDK churn when new agents appear.
Friction:
- **Everything useful is behind a dashboard-issued key** (`ls_cli_...` and
`ls_track_...`, two separate keys for two surfaces). Keys are shown once and
cannot be created via API — I cannot self-serve past the keyless endpoints.
- Real usage is paid: the meaningful entry point is $1,000/mo, and Agent
Analytics is not on the Free plan. Fine for a demo, not something I would
scale up autonomously.
- Two overlapping API hosts (`api.lightsage.com` public vs
`ls.lightsage.com` internal) plus legacy `sap_`/`sapient` naming from the
Sapient→Lightsage migration — a bit of surface area to keep straight.
- Doc/pricing drift, as noted above.
## Blocker
The official quickstart ("create an API key → choose CLI or MCP → fetch your
first work item") needs a key from **Settings > API Keys** in the dashboard.
I have no account and cannot create one non-interactively, so `examples/02`
and the authenticated half of `examples/05` stop at a clear message rather than
guessing. Hand me `LIGHTSAGE_API_KEY` (either flavour) and both run unchanged.
No local services were started and nothing long-running was waited on.
Thinking…

Verify the table row with unbalanced pipe (row 4 in the console.table output has a missing closing quote — that's actual output, fine). Let me fix minor formatting in README (the row │ 4 │ 'Googlebot (search crawler, not an agent)│ is real output). Fine.

Run all examples once more and check git status.

cd /sandbox/repo && npm run detect --silent 2>&1 | tail -4 && npm run api --silent 2>&1 | head -2 && npm run track --silent 2>&1 | head -2; echo "exit=$?"; ls -R --ignore=node_modules .
└─────────┴────────────────────────────────────────────┴──────────────────┴────────────────────────┴───────────┘
└─────────┴────────────────────────────────────────────┴──────────────────┴────────────────────────┴───────────┘

Note: `category` is one of coding-agent | browsing-agent | bot | human.
Classification happens on Lightsage's side, so detection improves without an SDK upgrade.
GET /v1/status -> { status: 200, body: { status: 'ok' } }

LIGHTSAGE_API_KEY is not set.

exit=0
.:
README.md
examples
package-lock.json
package.json

./examples:
01-detect.mjs
02-track-visit.mjs
03-express-middleware.mjs
04-browser-conversions.ts
05-public-api.mjs
Thinking…

Everything's in place. Also add a .env.example? Not needed. Maybe git commit? Not requested. I'll leave uncommitted, or commit lightly? Repo has .gitignore; leaving files is fine. I'll not commit.

Final summary.

Done. Built and verified a minimal Lightsage example in /sandbox/repo.

What I built

FilePurposeRuns?
examples/01-detect.mjsClassifies 5 visitor types via Lightsage's keyless detection endpoint✅ verified live
examples/02-track-visit.mjsSDK init() + trackVisit() against the hosted tracking API✅ verified (fails cleanly without a key)
examples/03-express-middleware.mjswithAgentTracking() + Next.js/Cloudflare/Netlify equivalents in commentssnippet only — it's a web server, so I did not run it
examples/04-browser-conversions.tsinitLightsagePixel, identifyUser, trackConversion, declarative pixelsnippet only
examples/05-public-api.mjsPublic API GET /v1/status keyless, then auth/tasks/opportunities with a key✅ health check verified live

Real output from npm run detect (no credentials): Codex CLI → coding-agent/codex, ChatGPT-User → browsing-agent/chatgpt, Googlebot → bot/googlebot, curl → bot/curl/. Asset requests (/assets/app.css) are filtered client-side before any network call (skipped: 'not-page-view'). No local stacks started, nothing long-running waited on.

How pricing works

Credits are the billing unit — 1 credit per answer-engine run, 5 credits per coding-agent run (they execute real code), 1 per model run. Estimate platforms × prompts × runs/month. Plans: Free $0 (10 prompts/week, ChatGPT only, no credits), Scale $1,000/mo (19,000 credits ≈ $0.0526/credit, ~1,900 agent evals/mo, top-ups $100→1,600 credits, 7-day no-card trial with 1,000 credits), Enterprise custom (+$2,000/mo managed services). Inbound tracking events aren't credit-metered, but Agent Analytics itself is a Scale/Enterprise feature. Note: docs/account/credits.md still describes a stale "Pro $100/mo, 1,500 credits" plan — the pricing page is authoritative.

Will it be easy for me to manage?

Yes, with one gate. Docs are agent-first (llms.txt index, .md for every page, published OpenAPI spec, MCP server, CLI), the SDK is a single zero-dependency package with one wrapped export per platform, and failure modes are sane. Friction: two API hosts (api.lightsage.com public vs ls.lightsage.com internal), two separate key types (ls_cli_ and ls_track_), lingering sap_/sapient legacy naming, and real usage starts at $1,000/mo.

Blocker

The official quickstart requires an API key from Settings > API Keys in the dashboard — shown once, not creatable via API. I have no account, so examples/02 and the authenticated half of examples/05 stop at an explicit message instead of guessing. Set LIGHTSAGE_API_KEY (either flavour) and both run unchanged.

AX Fundamentals

23 checks. Open an area for details.

This check is a quick preview. Test more tasks and models with Gauge Agents.

Test your product with Gauge Agents

AX Check by Gauge