# Lodd Documentation

Last updated: 2026-05-02

## Quick start

One prompt to set up: tell your coding agent "Add lodd.dev analytics to this project." It handles authentication, script embedding, and deployment. See [/llms.txt](/llms.txt) for the full agent setup flow.

## Tracking script

The tracking script is a single `<script>` tag added to your HTML `<head>`:

```html
<script defer src="https://lodd.dev/tracking/v1.js"
  data-site-id="YOUR_SITE_ID"
  data-tracking-secret="YOUR_SECRET"></script>
```

This automatically tracks page views, session duration, SPA navigations, referrers, UTM parameters, device info, and page load time. No cookies. Country-only geo. Bots are detected and tagged.

## Custom events

Track any user action with `window.ca.track()`:

```js
window.ca.track("event_name", { key: "value" })
```

- `event_name` — string, max 100 characters
- `properties` — optional object, max 10KB when serialised

Events count towards your monthly view limit alongside page views.

### Revenue tracking

Attach revenue to any event by including `revenue` and `currency` in properties:

```js
window.ca.track("purchase", {
  revenue: 49.99,
  currency: "EUR",
  plan: "pro"
})
```

- `revenue` — number (extracted from properties, stored separately for aggregation)
- `currency` — 3-letter ISO 4217 code (e.g. "EUR", "USD", "GBP")

Revenue fields are automatically extracted from properties and stored in dedicated columns. They appear in `get_event_counts` as `total_revenue`, `avg_revenue`, and `revenue_currency`.

Other properties (like `plan` in the example above) are kept in the properties object as usual.

### Common patterns

**E-commerce funnel:**
```js
window.ca.track("add_to_cart", { product: "Widget", revenue: 29.99, currency: "USD" })
window.ca.track("checkout_start")
window.ca.track("purchase", { revenue: 29.99, currency: "USD", order_id: "ORD-123" })
```

Then query the funnel:
> "Show me the conversion funnel from add_to_cart to checkout_start to purchase"

**Scroll depth:**
```js
const tracked = new Set();
const observer = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting && !tracked.has(e.target.id)) {
      tracked.add(e.target.id);
      window.ca.track("scroll_depth", { depth: e.target.id });
    }
  });
});
["25", "50", "75", "100"].forEach(pct => {
  const el = document.getElementById(`scroll-${pct}`);
  if (el) observer.observe(el);
});
```

Place invisible markers in your content:
```html
<div id="scroll-25" style="height:0"></div>
<!-- ... content ... -->
<div id="scroll-50" style="height:0"></div>
```

**Site search:**
```js
function onSearch(query) {
  window.ca.track("site_search", { query: query.slice(0, 200) })
  // ... your search logic
}
```

Then ask your agent: "What are the most common search terms on my site?"

**Form submissions:**
```js
form.addEventListener("submit", () => {
  window.ca.track("form_submit", { form: "contact", page: location.pathname })
})
```

**Signup / login:**
```js
window.ca.track("signup_click", { method: "google" })
window.ca.track("signup_complete", { plan: "free" })
```

## MCP tools reference

28 tools total: 2 authentication + 26 authenticated.

### Authentication (unauthenticated)
| Tool | Description |
|------|-------------|
| `authenticate(email)` | Send verification code to email |
| `verify_code(email, code)` | Exchange code for API key |

### Sites
| Tool | Description |
|------|-------------|
| `list_sites()` | All sites you own |
| `create_site(name, domain)` | Returns site ID, tracking secret, script tag |

### Analytics
| Tool | Description |
|------|-------------|
| `get_snapshot(site)` | Today vs yesterday quick comparison |
| `get_analytics(site, period, filters?)` | Aggregate stats with period comparison |
| `get_timeseries(site, period, interval?, filters?)` | Hourly or daily time buckets |
| `get_funnel(site, period, steps, filters?)` | Multi-step conversion funnel (pageviews + events) |
| `get_realtime(site)` | Active visitors in last 5 minutes |
| `get_performance(site, period, group_by?)` | Page load time avg/median/p95 |

### Breakdowns
| Tool | Description |
|------|-------------|
| `get_pages(site, period, url_contains?, filters?)` | Top pages with bounce rate + avg duration |
| `get_traffic_sources(site, period, filters?)` | Referrers, UTM, trackable links with engagement |
| `get_countries(site, period, filters?)` | Visitors by country with bounce rate |
| `get_tech_breakdown(site, period, filters?)` | Browser, OS, device distribution |
| `get_entry_exit_pages(site, period, filters?)` | Where sessions start and end |
| `get_bot_report(site, period)` | Bot/crawler traffic by user agent |

### Conversion attribution
| Tool | Description |
|------|-------------|
| `get_conversion_pages(site, event_name, period)` | Pages viewed before a conversion, with rate + time |
| `get_source_conversions(site, event_name, period)` | Traffic sources ranked by conversion rate |

### Custom events
| Tool | Description |
|------|-------------|
| `get_event_counts(site, period, filters?)` | Event totals, sessions, revenue |
| `get_events(site, period, event_name?, limit?)` | Individual event records with properties |
| `get_event_timeseries(site, event_name, period, filters?)` | One event over time |

### Usage
| Tool | Description |
|------|-------------|
| `get_usage()` | Plan, views used, monthly limit |

### Key management
| Tool | Description |
|------|-------------|
| `create_api_key(name?)` | Generate a new API key (shown once) |
| `list_api_keys()` | All keys with status and last used |
| `revoke_api_key(key_id)` | Permanently deactivate a key |

### Trackable links
| Tool | Description |
|------|-------------|
| `create_trackable_link(site, destination_url, source_type, label?)` | Short URL with source attribution |
| `list_trackable_links(site, status?)` | All links with click stats |
| `get_link_clicks(link, period)` | Click data for one link |

## Filters

Most analytics and breakdown tools accept optional filters:

| Filter | Format | Example |
|--------|--------|---------|
| `filter_country` | 2-letter ISO code | `"US"`, `"DE"`, `"NO"` |
| `filter_browser` | Substring match | `"Chrome"`, `"Safari"` |
| `filter_os` | Substring match | `"iOS"`, `"Windows"` |
| `filter_device_type` | Exact match | `"desktop"`, `"mobile"`, `"tablet"` |
| `filter_utm_source` | Exact match | `"twitter"`, `"newsletter"` |
| `filter_referrer_contains` | Substring match | `"google"`, `"reddit"` |

Breakdown tools exclude the dimension they group by (e.g. `get_countries` doesn't accept `filter_country`).

## Period formats

| Format | Meaning |
|--------|---------|
| `"today"` | Since midnight |
| `"yesterday"` | Previous full day |
| `"7d"` | Last 7 days |
| `"30d"` | Last 30 days |
| `"90d"` | Last 90 days |
| `"YYYY-MM-DD..YYYY-MM-DD"` | Custom range (up to 365 days) |

## Privacy

- No cookies
- No fingerprinting
- Country-only geolocation (no city, no coordinates)
- IPs are hashed with a daily-rotating salt, deleted after 24 hours
- GDPR compliant without consent banners
- Bot traffic is tagged and excluded from analytics

## Pricing

Free up to 2,500 views/month (page views + events combined). €9.99/month for 100,000 views. All features on both tiers. See [/pricing.md](/pricing.md) for details.
