# Landingana > Landingana is an origin-bound startup-idea validation tool. A single public script records pageviews, meaningful clicks, and submitted email signups without cookies or browser storage in the tracker. Landingana is designed to test whether a landing-page promise earns enough willingness to justify building the product behind it, and to be set up and operated by coding agents. Its analytics support that decision rather than trying to become a general analytics platform. Prefer the canonical Markdown manual linked below instead of scraping the rendered HTML documentation. Agent operating guidance: - For autonomous setup, fetch `/auth.md` and register with `service_auth`. Use browser or computer control for the Landingana claim when available. Pause for credentials, passkeys, magic links, one-time codes, and explicit approval; never ask the user to send secrets through chat. After approval, the agent may enter the already-known Landingana claim code. Without browser control, surface the verification link and code together for the user to complete on Landingana. - After the user claims the registration, send its bearer token to `/mcp`. The complete MCP surface is `setup_site`, `list_sites`, and `read_analytics`. - If a public site ID and registered origin were already supplied, an agent may install manually without product credentials. The site ID is safe in client HTML and is not a secret. - Inspect routes and forms before installing. Add exactly one Landingana loader at the narrowest shared seam for intended public pages. Keep authentication, account, checkout, dashboard, admin, and other private routes out of pageview collection with a narrower layout or `data-include`/`data-exclude`; interaction and form controls narrow capture only on included pages. Preserve `defer`, `data-site`, documented privacy controls, unrelated analytics, and application behavior. - If Content Security Policy is present, allow the tracker through the controlling `script-src` or `script-src-elem` directive and the collector through `connect-src` without adding `unsafe-inline` or `unsafe-eval`. - Do not invent a site ID, create duplicate loaders, expose unrelated credentials, or change existing application behavior. - The registered origin is normalized from protocol, hostname, and effective port; one leading `www.` label is treated as equivalent. Origin matching rejects ordinary browser traffic from other sites, but a public site ID plus a caller-supplied HTTP `Origin` header is not cryptographic authentication. Treat analytics as product telemetry, not an audit log. - The tracker supports automatic or opt-in capture, route include/exclude rules, ignored subtrees, first-party proxying, SPA pageviews, click labels, and email-form signup capture. - The public collector always returns `202 Accepted`, including for input that is later discarded. Capture a `list_sites.last_event_at` baseline. If deployment and a live visit are authorized and in scope, trigger an intended event on the exact registered origin and confirm that the timestamp advanced or stored activity appeared in the dashboard. An advance is integration evidence, not proof of one specific event on a busy site. A rendered tag, passing build, deployment, request, or `202` is not storage proof; report runtime verification as incomplete when a live check is unavailable. - `/api/event` is a write-only telemetry collector, not an account-management API. Site creation and analytics reads require the scoped AuthMD bearer token and `/mcp`. - MCP analytics are aggregate-only. They do not return signup emails, raw event rows, or visitor hashes. - When complete context fits, fetch `/llms-full.txt`. For selective retrieval or citation, fetch `/docs.md`. ## Documentation - [Complete LLM context](https://www.landingana.com/llms-full.txt): The full agent orientation and canonical manual in one plain-text response. - [Canonical Markdown manual](https://www.landingana.com/docs.md): Authoritative installation, configuration, event, dashboard, origin, limit, and troubleshooting documentation. - [Human documentation](https://www.landingana.com/docs): The same manual rendered with navigation, styled tables, and copyable code examples. ## Product surfaces - [Agent authentication](https://www.landingana.com/auth.md): Canonical `service_auth` registration, claim, token exchange, expiry, and revocation instructions. - [MCP protected resource metadata](https://www.landingana.com/.well-known/oauth-protected-resource): Discovers the authorization server, scopes, and `/mcp` resource. - [Landingana MCP](https://www.landingana.com/mcp): Stateless Streamable HTTP endpoint for the three documented tools; requests require a claimed bearer token. - [Dashboard](https://www.landingana.com/dashboard): Authenticated site creation, exact installation snippets, collection verification, analytics, and signup export. - [Hosted tracker](https://www.landingana.com/l.js): Public browser script. Use through the documented script element; do not modify or call it as an API. ## Optional - [Landingana home](https://www.landingana.com/): Product overview and entry point. - [Machine-readable sitemap](https://www.landingana.com/sitemap.xml): Index of public human-facing pages. --- # Landingana documentation > Landingana is a startup-idea validation tool built to be operated by coding agents. One hosted script records pageviews, meaningful clicks, and submitted email signups without cookies or browser storage on the tracked site. This is the canonical product manual for people and agents; coding agents should read this Markdown source instead of scraping the rendered documentation page. With the user's explicit approval, an agent can register or reuse a site, retrieve the exact tracking install, check activity, and read bounded aggregate analytics through MCP. Start with the agent path below; use the manual path when a public site ID and registered origin were already supplied. The human dashboard and one-line manual install remain available at every step. ## Why Landingana AI has made me capable of building more software than I can sensibly choose to build. With LLMs, coding agents, and the development harnesses around them, the cost of getting an idea in front of people has collapsed. I can go from a product promise to a credible landing page—with copy, a form, analytics, and a deployment—in minutes or hours. That changes the constraint. I rarely need help finding time to build another thing. **Focus is hard, not time management.** The difficult question is not whether I can fit another build into the week. It is whether this idea deserves the week at all. When building feels this easy, it is tempting to mistake momentum for evidence. I can spend days turning an idea into convincing software without learning whether anybody wants it. Before I commit to the product, I want some reason to believe the promise matters to somebody besides me. ### How I test an idea My test is simple. I create a landing page that explains the idea and gives the visitor one real action to take. They can leave an email address, join the first launch, create an initial account, or sign up for a free offering. Then I send real traffic to the page and watch what people do. Those actions are not equally strong signals. Creating an account asks more than joining a launch list. What matters at this stage is that each asks the visitor to move beyond passive interest and volunteer a way to continue. The landing page is cheap. The willingness to act is the signal. Pageviews tell me that people reached the promise. Deliberately tracked clicks show where it prompted a meaningful next action. A signup is stronger: somebody accepted the friction of sharing an email address or creating an account for the chance that the promise becomes real. If the people I intended to reach arrive and nobody signs up, that is useful too. It is evidence against the current combination of audience, promise, and offer before I commit to a full build. It does not automatically kill the underlying idea. It tells me to change the test or stop. ### The threshold I use A signup does not prove that the idea will become a successful company. It does not prove that people will pay, keep using the product, or recommend it. It is the signal I have chosen for a narrower decision: whether this idea has earned the next round of my effort. I do not need a universal conversion benchmark for that decision. I need to see whether the offer earns signups from the people I intended to reach, in the context of how many arrived and where they came from. That is more useful to me than a large analytics surface full of measurements that do not change what I build next. ### Why I built Landingana I built Landingana for this exact loop: launch a focused promise, send it traffic, observe the few interactions that matter, and decide whether the response is strong enough to build the product behind it. Landingana uses analytics, but it is designed around a founder's decision rather than a mature company's reporting needs. Its job is not to measure everything. Its job is to make one early experiment quick to instrument and easy to read: did the people I hoped to reach merely visit, did they act, and did any of them choose to continue? The dashboard is not the point. The point is shortening the path from a product promise to an evidence-backed decision: build the next version, revise the test, or walk away. AI gives me the leverage to make almost anything. I built Landingana to help me decide what is worth making. ## Quick start The default setup path is agent-operated: give a coding agent the target domain and let it discover the current authorization flow from `auth.md`. | Your agent does | You approve | Landingana enforces | | --- | --- | --- | | Discovers the flow, registers the connection, sets up the site, installs the snippet, and reads aggregate results. | The connection in Landingana after signing in with the same email used by the agent. | Account ownership, scoped tools, origin binding, expiring credentials, and dashboard revocation. | There are two supported installation paths: - **Autonomous setup:** start at `https://www.landingana.com/auth.md`, complete the user-claimed `service_auth` flow, then call `setup_site` through the Landingana MCP. - **Manual setup:** use a public site ID and registered origin supplied from the Landingana dashboard. For autonomous setup, the agent registers the connection and hands off one account-ownership step to the user: 1. Fetch `/auth.md` and register with the user's email as `login_hint`. 2. Use browser or computer control to open the verification link when available. Pause for the user's credentials, passkey, magic link, one-time code, or explicit approval; never ask the user to send secrets through chat. 3. After the user authenticates, reviews the Landingana claim screen, and explicitly approves continuing, the agent may enter the six-digit claim code it already received. Without browser control, show the link and code together so the user can complete the claim on Landingana. 4. Poll the documented claim grant until Landingana returns an access token and identity assertion. 5. Connect to `https://www.landingana.com/mcp` with the bearer token and call `setup_site` with the target web origin. 6. Return to the target repository. Inspect routes and forms, then add one Landingana loader at the narrowest shared seam for intended public pages. Keep authentication, account, checkout, dashboard, admin, and other private routes out of pageview collection with a narrower layout or `data-include`/`data-exclude`; interaction controls alone do not suppress pageviews. 7. Capture the site's `list_sites.last_event_at` baseline. If deployment and a live visit are authorized and in scope, visit the exact registered origin, trigger an intended event on a controlled public route, and confirm that the timestamp advanced or stored activity appeared in the dashboard. Treat an advance as integration evidence rather than proof of one specific event on a busy site. Otherwise report runtime verification as incomplete. Use `read_analytics` for bounded aggregate results. ### Prefer not to connect an agent? Manual installation can proceed without product credentials once you or an agent has two values: - The public Landingana site ID, such as `a1b2c3d4`. - The registered site origin, such as `https://example.com`. The site ID is intentionally public and belongs in client HTML. It is not a secret or API key. Never invent a site ID: if one has not been supplied, obtain it from the site's Landingana dashboard before editing the target application. 1. Find the narrowest shared document, head, or layout that covers only the intended public pages. 2. Audit every route and email form covered by that seam. Keep private-route pageviews out with a narrower layout or `data-include`/`data-exclude`. Use opt-in mode or element/form controls separately to narrow clicks and signup capture on included pages. 3. Add the generated script exactly once. Preserve `defer`, the supplied `data-site` value, documented privacy controls, unrelated analytics, and application behavior. 4. If Content Security Policy is present, allow the tracker in the controlling `script-src` or `script-src-elem` directive and the collector in `connect-src` without adding `unsafe-inline` or `unsafe-eval`. 5. Run the target project's checks and verify that an intended rendered page contains exactly one Landingana loader. 6. If a live visit is authorized and in scope, visit the registered production or preview origin and confirm stored activity in the dashboard. A rendered tag, passing build, deployment, network request, or HTTP `202` is not storage proof. If no live check is available, report runtime verification as incomplete. **Basic HTML installation** ```html ``` Replace `YOUR_SITE_ID`; do not copy that placeholder into a real deployment. > **Local development** > > The tracker skips `localhost` and `127.x.x.x` by default. `data-debug` bypasses only that client-side skip; it does not bypass server-side origin binding. It can exercise client emission, but `202` still does not prove storage. If the plan's site allowance permits, use a separate site registered to the exact local protocol, hostname, and port. Free users who already registered production should verify storage on that deployed origin instead. A wrong origin can be corrected from its site page before the first event. ### Next.js App Router Prefer a layout in the public route group. If the root layout is the only shared seam, add route filters so private pages do not emit pageviews. Next.js forwards custom data attributes to the rendered script element. ```tsx import Script from "next/script"; ``` ### Content Security Policy For the hosted installation, the policy that governs the page must allow `https://www.landingana.com` in `script-src` or `script-src-elem` and in `connect-src`. A first-party proxy uses the tracked site's own script and connection origins instead. Preserve existing nonces and strict directives; do not make the policy broadly permissive with `unsafe-inline` or `unsafe-eval` to install analytics. ## Agent authentication and MCP Landingana's remote MCP is intentionally small. Authentication is additive to the existing Better Auth login system: people continue to sign in with GitHub or a magic link, while agents use AuthMD's user-claimed `service_auth` flow. Landingana does not accept anonymous agent registration or incoming provider identity assertions. ### Start with `auth.md` The agent walkthrough is: ```text https://www.landingana.com/auth.md ``` It contains current endpoint URLs, grant names, polling rules, and recovery instructions. Supporting discovery endpoints are also public: | Surface | URL | | --- | --- | | Protected resource metadata | `/.well-known/oauth-protected-resource` | | Authorization server metadata | `/.well-known/oauth-authorization-server` | | Public signing keys | `/.well-known/jwks.json` | | Agent registration | `/agent/identity` | | Claim-code replacement | `/agent/identity/claim` | | Token exchange | `/oauth2/token` | | Token revocation | `/oauth2/revoke` | | Streamable HTTP MCP | `/mcp` | Register with a valid email login hint: ```http POST /agent/identity Content-Type: application/json {"type":"service_auth","login_hint":"user@example.com"} ``` The registration response is deliberately account-existence-agnostic. It does not reveal whether that email already belongs to a Landingana user. The response includes a claim token plus a verification URL and six-digit user code. Treat `claim_token` as private bearer material: never log, render, or show it to the user. Surface only the intended `verification_uri` and `user_code`; possession of the outer token lets the agent poll for credentials after approval. With browser or computer control, the agent opens the verification link and pauses whenever Landingana needs credentials, a passkey, magic-link access, a one-time login code, or explicit approval. After the user authenticates, reviews the claim screen, and approves continuing, the agent may enter the six-digit claim code it already received. Without browser control, the agent shows the link and code together so the user can complete the claim on Landingana. An existing account binds the registration to the same user, sites, plan, and usage. A new user is created only through the normal human sign-in flow. No usable API credential or site is created before the authenticated user confirms the claim, and the verified account email must match the original login hint. > **Keep the code on Landingana** > > The claim code is already known to the registering agent; account credentials, passkeys, magic links, and one-time login codes are not. Never ask the user to paste any secret into the harness or chat. Claim attempts expire, are attempt-limited, and can be replaced only while the outer registration remains valid. A replacement response contains a new pair under `claim_attempt`; discard the expired verification URL and code, then repeat the claim ceremony with the new values. ### Credential lifecycle The default scopes are `sites:read`, `sites:write`, and `analytics:read`. - Access tokens last one hour and are sent as `Authorization: Bearer …` to `/mcp`. - The returned identity assertion lasts 30 days and can be exchanged for another access token using the JWT bearer grant documented in `/auth.md`. This renews access from the assertion; it is not a refresh-token flow. - Landingana does not issue refresh tokens. When the assertion expires or exchange returns `invalid_grant`, discard the credentials and repeat `service_auth` registration and user claim. - `/oauth2/revoke` revokes one access token. The user can revoke the entire agent registration—including every token and identity assertion—from the dashboard. ### MCP tools The MCP is stateless and exposes exactly three scope-gated tools: | Tool | Required scope | Input | Behavior | | --- | --- | --- | --- | | `setup_site` | `sites:write` | `origin` | Normalizes the web origin, creates, recovers, or reuses the user's site idempotently, respects the account's site limit, and returns the exact installation snippet plus whether a legacy row was recovered. | | `list_sites` | `sites:read` | Optional `cursor` | Returns at most 20 owned sites per page with tracking state, last activity, plan, site allowance, and current-month event usage. | | `read_analytics` | `analytics:read` | `site_id`, optional `range`, optional `detail` | Returns aggregate analytics for an owned site. Ranges are `24h`, `7d`, or `30d`; detail is `summary` or `full`. | `read_analytics` summary mode returns visitors, pageviews, signups, and signup rate. Full mode additionally returns at most 30 time buckets and five rows for each ranked breakdown: pages, referrers, countries, devices, browsers, and clicks. Returned click labels are bounded and email-redacted; when the entire value is URL-shaped, credentials, query strings, fragments, and contact schemes are removed. This read-time pass also protects legacy rows created before write-time sanitization. The MCP never returns signup email addresses, raw events, visitor hashes, or signing material. It does not expose billing, checkout, deletion, raw SQL, token management, documentation resources, prompts, or extra tools. Fetch `/llms.txt`, `/llms-full.txt`, or `/docs.md` directly when more product guidance is needed. The public `/api/event` collector remains a separate write-only browser telemetry endpoint. It is not an authentication or account-management API. ## Overview Landingana helps determine what turns a landing-page visit into intent. One hosted script records: | Signal | What it means | | --- | --- | | Pageview | An initial page load or a URL-changing `history.pushState`, `history.replaceState`, or browser back/forward navigation. | | Click | A link, button, or element explicitly labeled with `data-track`. | | Signup | A submitted form containing a filled `input[type="email"]`. | The tracking script has no runtime dependencies and does not use cookies, `localStorage`, or other browser storage. It sends events with `navigator.sendBeacon`, falls back to a keepalive `fetch`, and swallows collection errors so analytics cannot break the host page. > **Privacy model** > > Landingana derives a pseudonymous site-scoped visitor hash from request and browser context. Its server-side salt rotates daily, so the same person may count again on another UTC day. Nothing is persisted in the visitor's browser by the tracking script. This reduces persistent tracking; it is not a legal-compliance guarantee for every site or jurisdiction. ### What an event can contain | Event | Browser data | Ingest context | | --- | --- | --- | | Pageview | Path, external referrer, UTMs, and browser language | Daily visitor, location when available, browser, OS, and device | | Click | Path, label, and destination URL | Daily visitor and device context | | Signup | Path and submitted email | Daily visitor and device context | External referrers are normalized to hostnames before storage. Signup capture observes the browser's submit event. It does not wait for a form provider to confirm success, call `preventDefault`, or change normal submission behavior. ## Install script The generated script is the complete default installation. `data-site` is required; the tracker exits immediately when it is missing or empty. ### Configured installation This example opts into explicit interaction capture, limits tracking to selected public routes, excludes account routes, and sends data through first-party proxy paths. ```html ``` ### First-party proxy mode in Next.js Proxy both the script and event endpoint through the tracked domain. Proxying only events does not help if a blocker prevents the third-party script from loading; proxying only the script without setting `data-endpoint` sends events to the wrong relative path. Use the configured script above together with these rewrites: ```ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { async rewrites() { return [ { source: "/_landingana/l.js", destination: "https://www.landingana.com/l.js", }, { source: "/_landingana/event", destination: "https://www.landingana.com/api/event", }, ]; }, }; export default nextConfig; ``` > **Preserve request context** > > A first-party proxy must preserve the browser `Origin`, `User-Agent`, client-IP forwarding, and any geographic headers Landingana is expected to use. Verify the rewrite behavior on the actual hosting platform. ## Complete configuration ### Script attributes | Attribute | Default | Behavior | | --- | --- | --- | | `src` | Required | Loads the hosted tracker or a first-party proxy. The default event endpoint is derived from this URL. | | `defer` | Recommended | Lets HTML parsing finish before a conventional script tag executes. It is included in generated snippets. | | `data-site` | Required | Public site ID. A missing or empty value disables the tracker. | | `data-lana-mode` | Automatic | Set exactly to `opt-in` to require explicit click and signup markers. | | `data-include` | All paths | Comma-separated exact paths and `/*` descendant patterns. Applies to every event type. | | `data-exclude` | No paths | Comma-separated exact paths and `/*` descendant patterns. Takes precedence over `data-include`. | | `data-endpoint` | Script origin plus `/api/event` | Relative or absolute HTTP(S) event URL. Invalid values fall back to the default endpoint. | | `data-debug` | Off | Attribute presence bypasses the tracker's local-host skip. The collector still requires a site registered to the exact local origin. | ### Element and form attributes | Attribute | Place it on | Behavior | | --- | --- | --- | | `data-track` | Any clickable element | Makes the element trackable and supplies a stable label. In opt-in mode, every tracked click requires it. | | `data-capture="false"` | Form | Disables email signup capture in automatic mode. | | `data-capture="true"` | Form | Enables signup capture in opt-in mode when a filled email input exists. | | `data-lana-ignore` | Element or ancestor | Suppresses click and signup capture in the subtree. It always wins and does not suppress pageviews. | ### Automatic versus opt-in mode | Behavior | Automatic | Opt-in | | --- | --- | --- | | Initial and SPA pageviews | Automatic | Automatic | | Link and button clicks | Automatic | Require `data-track` | | Email form submissions | Automatic unless disabled | Require `data-capture="true"` | | Ignored subtree | Always excluded | Always excluded | ## Targeting and capture ### Target specific pages Add a comma-separated path allowlist to the script. Matching uses `location.pathname`, so query strings and fragments do not affect a rule. ```html ``` Use `data-exclude` by itself to track every path except selected routes. If both attributes are present, an excluded path is never tracked. ```html ``` | Pattern | Matches | Does not match | | --- | --- | --- | | `/pricing` | `/pricing` | `/pricing/enterprise` | | `/docs/*` | `/docs` and descendants | `/docs-old` | | `/` | Homepage only | Any other path | ### Label only the actions that matter ```html