# 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"; ``` ### 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
``` Click labels use the `data-track` value first, then trimmed element text, and are limited to 80 characters. Prefer stable semantic labels so dashboard reporting survives visible copy changes. > **SPA navigation** > > Landingana records a pageview when `history.pushState`, `history.replaceState`, or browser back/forward changes `pathname + search`. State-only calls, repeated URLs, and hash-only changes do not create duplicate pageviews. ## Events and data Every tracked interaction is a `pageview`, `click`, or `signup`. Context is collected only when it helps explain intent. | Field | Applies to | Notes | | --- | --- | --- | | Path | All events | Current pathname, up to 512 characters. | | Visitor | All events | Anonymous site-scoped hash derived with a salt that rotates daily. | | Referrer and UTMs | Pageviews | Same-host referrers are excluded. External referrers are normalized to hostnames; source, medium, and campaign are retained. | | Click label and URL | Clicks | Label up to 80 characters and destination up to 512. New values are email-redacted before storage. Whole URL-shaped values are reduced to a non-credentialed scheme, host, and path; contact schemes are replaced. Prose labels are preserved, so avoid embedding sensitive query strings in custom labels. | | Email | Signups | Valid emails are normalized to lowercase in the signup list. | | Location and device | All events | Country, region, and city when platform headers exist; browser, OS, and device derived from `User-Agent`. | Detected bots, invalid payloads, unknown or paused sites, origin mismatches, rate-limited requests, and over-quota traffic are silently dropped. The collector still returns `202 Accepted` to avoid leaking whether a site ID exists and to avoid breaking a visitor's page. **A `202` response proves only that the collector handled the request. It does not prove that the event passed validation or was stored.** Verify storage in the dashboard. For new ingestion, account quota, per-site usage, an optional signup row, and the corresponding event are written in one PostgreSQL statement. Invalid signup email payloads are rejected before quota is claimed, and a write failure rolls the statement back. Historical counters from before this guarantee are not retroactively reconciled. ## Dashboard The account dashboard is the index of experiments. Each site row shows pageviews and signups from the last seven days, then opens the detailed view. ### Time ranges and KPIs Choose 24 hours, 7 days, or 30 days. The selected UTC window drives the four KPI cards, time series, and ranked breakdowns. | KPI | Definition | | --- | --- | | Visitors | Distinct daily visitor hashes among pageviews | | Pageviews | Stored pageview events | | Signups | Stored signup events | | Signup rate | Signups divided by unique visitors | ### Breakdowns Top pages, referrers, countries, devices, browsers, and clicks show leading values in the active window. Query results contain up to ten values; the current dashboard presents shorter visible lists for some device and browser breakdowns. Missing referrers appear as direct, and unavailable dimensions appear as unknown. ### Recent signups and CSV Recent signups shows the newest 50 signups across all time, including email, path, and relative time. It is not filtered by the chart range. Export CSV downloads every signup for the site, oldest first, with ISO 8601 UTC timestamps. Fields are RFC 4180 encoded and spreadsheet formula prefixes are neutralized. The detail page checks for updated data every 30 seconds while visible and when the user returns to it. This is polling, not a streaming or instant-delivery guarantee. Manual refresh and a retry boundary remain available. ## Site details ### A site is a web origin Landingana stores the normalized protocol, hostname, and effective port—not paths, credentials, query strings, or fragments. Incoming browser events are accepted only when their `Origin` matches the registered origin after normalization. | Equivalent origins | Different origins | | --- | --- | | `https://example.com` and `https://www.example.com` | `http://example.com` and `https://example.com` | | `https://example.com` and explicit port `:443` | `https://app.example.com` and `https://example.com` | | One leading `www.` label and the otherwise identical hostname | Default and non-default ports | > **Public site ID, private analytics** > > The site ID in the script is public by design. Origin matching is a browser admission and noise-reduction boundary: it rejects ordinary cross-origin browser telemetry, but a direct client can supply an HTTP `Origin` header. It is not cryptographic event authentication, and Landingana analytics are not an audit-grade integrity log. Authenticated ownership still protects dashboards, agent reads, and exports. ### Before the first event The site page shows the exact registered origin, script, and a copyable coding-agent prompt with privacy-safe placement and stored-event verification instructions. After events arrive, the analytics view keeps the same installation information in a persistent disclosure instead of hiding it. Before the first event, the owner can correct a mistaken protocol, hostname, or port on that site without consuming another site allowance. Once an origin-bound site has analytics history, its origin is locked so existing data cannot be relabeled. ### Legacy sites Sites created before origin binding have no trusted origin and safely ignore new events. They never receive a non-working snippet. When an account has no origin-bound site, normal setup assigns the explicit public URL to the deterministic oldest legacy row, preserves its site ID and history, and resumes tracking. `setup_site` reports this as `recovered: true`. An owner can also open any specific legacy site and assign its public origin explicitly, including a history-bearing record. If another site already occupies the Free active slot, that newly connected record remains paused rather than creating a second active collector. ## Pricing and limits Both plans contain the analytics product. They differ in total site and monthly event allowances. | Plan | Price | Sites | Monthly event allowance | | --- | --- | --- | --- | | Free | $0 | 1 | 10,000 | | Founding Pro | $9 monthly or $90 yearly | Unlimited | 100,000 pooled across the account | Founding Pro is the configured paid plan, but public paid signup may be closed. Do not assume an agent or user can start checkout merely because pricing is documented; use the current dashboard state as the source of truth. ### What counts Each event stored through the current atomic ingestion path consumes one unit. Pro usage is pooled across the account: one site can use the allowance or many sites can share it. Unlimited refers to sites, not traffic. At the limit, Landingana stops storing new events until the next UTC month or a plan change raises the ceiling. There are no automatic overages, and dropped events are not replayed. ### Downgrades and payment grace When Pro access ends, historical data and every site are preserved. Landingana keeps the oldest origin-bound site active and pauses the rest; if every row is legacy, it falls back to the deterministic oldest row. Active and trialing subscriptions receive Pro access; past-due subscriptions receive seven days of grace from the new billing period's start. ## Troubleshooting and FAQ ### I installed the script, but no events appear Check the exact site ID, registered protocol/hostname/port, `data-include` allowlist, `data-exclude` blocklist, monthly usage, and whether the site is paused. Remember that one leading `www.` label is normalized as equivalent. For local storage, `data-debug` still needs a site registered to the exact local protocol, hostname, and port. Create that extra site only when the plan allows; otherwise verify the registered deployed origin. A `202` response does not prove that an event was stored. ### Pageviews appear, but clicks do not Automatic mode covers links and buttons. Opt-in mode requires `data-track`. Also check whether the element or an ancestor has `data-lana-ignore`. ### My form submits, but no signup appears The form needs a filled `input[type="email"]`. Remove `data-capture="false"` in automatic mode, add `data-capture="true"` in opt-in mode, and check ignored ancestors. Signup capture records the submit attempt rather than waiting for a remote form provider to report success. ### Why did only one site stay active after cancellation? Free includes one site for new setup. If a Pro account later returns to Free, Landingana preserves every site and its history, keeps the oldest usable origin-bound site active, and pauses the rest. Restoring Pro restores collection across them. ### What happens at the monthly limit? New events are dropped without affecting the visitor's page. Collection resumes next UTC month or after a plan change raises the same month's ceiling. There are no overage fees or retroactive recovery. ### Can a blocker stop Landingana? Yes. First-party mode can improve compatibility, but proxy both the script and endpoint and preserve request headers. No analytics tool can guarantee collection against every blocker. ### Does Landingana use cookies? The public tracking script does not use cookies, `localStorage`, or browser storage. The authenticated Landingana application has its own session behavior. Review the tracked site's complete data practices and applicable requirements before making legal claims. ### Can an agent create sites or read analytics programmatically? Yes. Start at `/auth.md`, complete the user-claimed `service_auth` flow, and use the authenticated `/mcp` endpoint. `setup_site`, `list_sites`, and `read_analytics` are the complete MCP surface. Do not infer additional management operations from the public event collector: `/api/event` is a write-only browser telemetry endpoint and deliberately returns the same `202` response for accepted and discarded input. ### Can an agent read signup email addresses? No. MCP analytics are aggregate-only and do not return signup emails, raw event rows, or visitor hashes. Signup email lists and CSV export remain authenticated dashboard workflows for the human account owner. ### The agent's assertion expired. Can it use a refresh token? No refresh token is issued. Discard the expired credentials and restart at `/agent/identity` so the user can claim a new `service_auth` registration. If an access-token request is temporarily rate-limited, honor the response's `Retry-After` value instead of starting duplicate registrations.