Documentation
Agent-operatedGive your agent the job. Keep control of the account.
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.
01 — The purpose
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.
02 — Start here
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-claimedservice_authflow, then callsetup_sitethrough 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:
- Fetch
/auth.mdand register with the user's email aslogin_hint. - 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.
- 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.
- Poll the documented claim grant until Landingana returns an access token and identity assertion.
- Connect to
https://www.landingana.com/mcpwith the bearer token and callsetup_sitewith the target web origin. - 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. - Capture the site's
list_sites.last_event_atbaseline. 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. Useread_analyticsfor 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.
- Find the narrowest shared document, head, or layout that covers only the intended public pages.
- 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. - Add the generated script exactly once. Preserve
defer, the supplieddata-sitevalue, documented privacy controls, unrelated analytics, and application behavior. - If Content Security Policy is present, allow the tracker in the controlling
script-srcorscript-src-elemdirective and the collector inconnect-srcwithout addingunsafe-inlineorunsafe-eval. - Run the target project's checks and verify that an intended rendered page contains exactly one Landingana loader.
- 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
202is not storage proof. If no live check is available, report runtime verification as incomplete.
Basic HTML installation
<script defer src="https://www.landingana.com/l.js" data-site="YOUR_SITE_ID"></script>Replace YOUR_SITE_ID; do not copy that placeholder into a real deployment.
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.
import Script from "next/script";
<Script
src="https://www.landingana.com/l.js"
data-site="YOUR_SITE_ID"
data-exclude="/login/*,/account/*,/checkout/*,/dashboard/*,/admin/*"
strategy="afterInteractive"
/>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.
03 — Agent access
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:
https://www.landingana.com/auth.mdIt 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:
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.
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 repeatservice_authregistration and user claim. /oauth2/revokerevokes 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.
04 — What it measures
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.
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.
05 — Manual install
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.
<script
defer
src="/_landingana/l.js"
data-site="YOUR_SITE_ID"
data-lana-mode="opt-in"
data-include="/,/pricing,/docs/*"
data-exclude="/dashboard/*,/login/*"
data-endpoint="/_landingana/event"
></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:
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;06 — Reference
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 |
07 — Control
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.
<script
defer
src="https://www.landingana.com/l.js"
data-site="YOUR_SITE_ID"
data-include="/,/pricing,/docs/*"
data-exclude="/docs/private/*"
></script>Use data-exclude by itself to track every path except selected routes. If both attributes are present, an excluded path is never tracked.
<script
defer
src="https://www.landingana.com/l.js"
data-site="YOUR_SITE_ID"
data-exclude="/login/*,/dashboard/*"
></script>| 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
<button data-track="Hero signup CTA">Start free</button>
<form data-capture="true">
<input type="email" name="email" required />
<button type="submit" data-track="Join waitlist">Join waitlist</button>
</form>
<section data-lana-ignore>
<!-- Clicks and email submissions here are ignored. -->
</section>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.
08 — Data model
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. |
| 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.
09 — Read the signal
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.
10 — Site lifecycle
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 |
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.
11 — Plans
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.
12 — Support
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.
Ready to hand off the setup?
Give your agent the target domain and point it at auth.md. You approve the connection; it handles the supported setup and analytics workflow.