Skip to content

Quickstart

View as markdown

Sign up at wirelog.ai, create a project, and grab your keys. Then paste the prompt below into your coding agent to get started.

Everything below is the same reference your agent reads from /llms.txt. You can use it directly too.


API Keys

PrefixNamePermissionsWhere to use
pk_PublicTrack onlyClient-side (browsers, mobile, agents)
sk_SecretTrack + query + identifyServer-side only — never expose
aat_Access tokenScoped (track/query/admin), time-limitedAgents, integrations

Auth: send as X-API-Key header or ?key= query param.


Setup

Browser Script Tag

<script src="https://cdn.wirelog.ai/public/wirelog.js" data-key="pk_YOUR_KEY"></script>

Auto-tracks page_view on load and SPA navigations. Auto-captures: url, previous_url, referrer, referring_domain, title, viewport, language, timezone, utm_source/medium/campaign/term/content, gclid, fbclid.

AttributeDescription
data-keyPublic key (required)
data-hostAPI URL override (defaults to script src origin)
data-consent"true" = require wl.optIn() before tracking
data-auto"false" = disable auto page_view
data-spa"false" = disable SPA navigation tracking

JS API:

wl.track("event_name", { key: "value" }) // custom event
wl.identify("user_id", { name: "Alice" }) // bind device to user + set profile
wl.reset() // clear identity (logout)
wl.optIn() / wl.optOut() // consent management
wl.flush() // manual flush

Identity: device_id in localStorage, session_id in sessionStorage (30-min timeout), user_id in localStorage. Batches 10 events or every 2s. sendBeacon on page close.

TypeScript / Node.js

Terminal window
npm install wirelog
import { wl } from "wirelog";
wl.init({ apiKey: "pk_YOUR_KEY" });
wl.track({ event_type: "signup", user_id: "u_123", event_properties: { plan: "free" } });
wl.identify({ user_id: "u_123", user_properties: { name: "Alice", plan: "pro" } });
await wl.close(); // flush on shutdown

Zero runtime dependencies. Node 18+ and all modern browsers. In browsers, shares identity with the script tag (same localStorage keys). Batches 10 events or every 2s with retry.

Python

Terminal window
pip install wirelog
from wirelog import WireLog
wl = WireLog(api_key="pk_YOUR_KEY")
wl.track("signup", user_id="u_123", event_properties={"plan": "free"})
wl.identify("u_123", user_properties={"name": "Alice", "plan": "pro"})
wl.close() # flush on shutdown

Zero external dependencies. Python 3.9+. Background thread batches 10 events or every 2s with retry. Context manager supported: with WireLog() as wl:.

Go

Terminal window
go get github.com/wirelogai/wirelog-go
import wirelog "github.com/wirelogai/wirelog-go"
client := wirelog.New(wirelog.Config{APIKey: "pk_YOUR_KEY"})
defer client.Close()
client.Track(wirelog.Event{EventType: "signup", UserID: "u_123", EventProperties: map[string]any{"plan": "free"}})
client.Identify(ctx, wirelog.IdentifyParams{UserID: "u_123", UserProperties: map[string]any{"name": "Alice", "plan": "pro"}})

Zero external dependencies. Go 1.22+. Track() is non-blocking — background goroutine batches 10 events or every 2s with retry. Queue capped at 10,000.

CLI

Terminal window
brew install wirelogai/tap/wl
# or: go install github.com/wirelogai/wirelog-cli@latest
Terminal window
wl config init # interactive setup
wl inspect # discover events
wl query "* | last 7d | count by event_type" # query
wl track page_view --user-id u1 --prop path=/home # track
wl identify --user-id u1 --prop plan=pro # identify

Auto-detects TTY: styled tables for humans, --json for agents. Config precedence: flags > env vars (WIRELOG_API_KEY/WIRELOG_HOST) > .wirelog.json > ~/.config/wirelog/config.json.

Raw HTTP (cURL)

Terminal window
# Track
curl -X POST https://api.wirelog.ai/track \
-H "X-API-Key: pk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"event_type":"page_view","user_id":"u_123","event_properties":{"page":"/pricing"}}'
# Query (requires sk_ key)
curl -X POST https://api.wirelog.ai/query \
-H "X-API-Key: sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"page_view | last 7d | count by _browser","format":"llm"}'
# Identify
curl -X POST https://api.wirelog.ai/identify \
-H "X-API-Key: pk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id":"u_123","device_id":"dev_abc","user_properties":{"plan":"pro"}}'

All SDKs support env vars: WIRELOG_API_KEY and WIRELOG_HOST (default: https://api.wirelog.ai).


Event Schema

{
"event_type": "string (required)",
"user_id": "string",
"device_id": "string",
"session_id": "string",
"time": "ISO 8601 (defaults to now)",
"event_properties": { "key": "string|number|bool|null" },
"user_properties": { "key": "string|number|bool|null" },
"insert_id": "string (auto-generated for deduplication)",
"clientOriginated": true
}

Batch: { "events": [ ...up to 2000 ] } Response: {"accepted": N} (invalid events silently skipped).

What to Track

  • SaaS: signup, login, feature_used (with event_properties.feature), subscription_started, payment_completed
  • E-Commerce: page_view, search, item_viewed, add_to_cart, checkout_started, purchase
  • AI Agent: agent_action, agent_error, token_usage, user_feedback
  • General: use event_properties for event data, user_properties/identify for user data, send device_id on every event

Identity & Profiles

Call POST /identify when a user is known (login/signup/account-link):

{
"user_id": "alice@acme.org",
"device_id": "dev_abc",
"user_properties": { "email": "alice@acme.org", "plan": "pro" },
"user_property_ops": {
"$set": { "plan": "pro" },
"$set_once": { "signup_source": "ads" },
"$add": { "login_count": 1 },
"$unset": ["legacy_flag"]
}
}

Response: {"ok": true}

  • distinct_id = coalesce(user_id, mapped_user_id, device_id) — stitched identity across anonymous and known users
  • Pre-identify anonymous events are retroactively attributed once the device is identified
  • Recommended profile fields — B2B: email, plan, company_id, company, account_tier; B2C: email, acquisition_channel, persona, country

Query Language

Structure

source | stage | stage | ...
Terminal window
POST /query {"q": "signup | last 7d | count by day", "format": "llm"}

Output formats: llm (Markdown tables, default), json, csv.

Discover Events First

Always discover before writing event-specific queries:

inspect * | last 30d -- event overview with properties
inspect signup | last 7d -- single event property detail
* | last 30d | count by event_type | top 20 -- lightweight event counts
fields | last 7d -- all available fields

Sources

SourceSyntaxDescription
Eventpage_view, "landing:cta_click", *Events by name. * = all. Quotes for special chars.
Funnelfunnel a -> b -> cConversion funnel. Optional: exclude x, y (between-step).
Retentionretention signupWeek-over-week cohort. Optional: returning <event>.
Pathspaths from signup or paths to purchaseEvent sequences. Use | by _path for URL nav flows.
SessionssessionsSession-level analysis.
Lifecyclelifecycle page_viewNew / Returning / Resurrected / Dormant segments.
Stickinessstickiness page_viewDays active per period distribution.
User timelineuser "alice@acme.org"Single user event history.
Users directoryusersUser profile listing.
Formulaformula count(a) / count(b)Ratios. Supports: count, unique, sum, avg.
FieldsfieldsAvailable field names including dynamic properties.
Inspectinspect * or inspect signupSchema discovery with coverage %, types, samples.

Stages

StageSyntax
Filter| where field = "value"
Operators=, !=, >, <, >=, <=, contains, not contains, ~ (regex), !~, in ("a","b"), not in, exists, not exists
Boolean| where (a = "x" or b = "y") and c = "z" — nested parentheses supported
Time range| last 7d / | last 12w / | from 2026-01-01 to 2026-02-01 / | today / | yesterday / | this week / | this month / | this quarter / | this year
Aggregation| count, | unique distinct_id, | sum field, | avg field, | min field, | max field, | median field, | p90 field, | p95 field, | p99 field
Group by| count by day, | count by week, _browser — granularities: hour, day, week, month, quarter
List| list — raw event rows
Sort| sort field desc
Limit| limit 100, | top 20 (top = sort desc + limit)
Window| window 7d — for funnel/paths (default 1h)
Depth| depth 8 — for paths
By| by _path — relabel path nodes by field instead of event_type

Fields

Core: event_type, user_id, distinct_id, device_id, session_id, time, insert_id

Page/Content: _url, _path, _path_clean (ID-normalized: /users/{id}/settings), _hostname, _title

Referrer: _referrer, _referrer_domain

Device: _browser, _browser_version, _os, _os_version, _platform, _device_type

Geography: _country, _city, _region, _continent, _timezone (short aliases). Full: _geo_country, _geo_region, _geo_region_code, _geo_city, _geo_continent, _geo_timezone, _geo_metro_code, _geo_postal_code, _geo_latitude, _geo_longitude

Other system: _ip, _ua, _library, _ingest_origin

Custom properties: event_properties.KEY, user_properties.KEY

User profile (from identify): user.email, user.email_domain, user.plan, user.first_seen, user.last_seen, user.KEY

Session (35 fields): session.start_time, session.end_time, session.duration, session.event_count, session.landing_url, session.landing_path, session.landing_path_clean, session.landing_hostname, session.exit_url, session.exit_path, session.exit_path_clean, session.referrer, session.referring_domain, session.utm_source, session.utm_medium, session.utm_campaign, session.utm_term, session.utm_content, session.gclid, session.fbclid, session.language, session.timezone, session.ingest_origin, session.country, session.city, session.region, session.continent, plus full session.geo_* variants

Latest stitched session: user_last_session.KEY — same key space as session.*, resolves to each user’s most recent session

Identity note: distinct_id = coalesce(user_id, mapped_user_id, device_id). Use unique distinct_id for user-level uniques.

Examples

Replace placeholder event names with your own. Run inspect * | last 30d first to discover them.

Discovery

inspect * | last 30d
inspect signup | last 7d
* | last 30d | count by event_type | top 20
fields | last 7d

Counts & trends

signup | last 7d | count
signup | last 30d | count by day
signup | last 90d | unique distinct_id by week
page_view | where _platform = "web" | last 30d | count
* | last 24h | count by event_type

Funnels

funnel signup -> activate -> purchase | last 30d
funnel signup -> activate -> purchase exclude support_ticket | window 7d
funnel signup -> activate -> purchase | last 30d | by _platform

Retention

retention signup | last 90d
retention signup returning core_usage | last 90d

Paths

paths from signup | window 7d | last 30d | depth 8
paths from page_view | last 30d | by _path
paths from page_view | last 30d | by _path_clean

Sessions & attribution

sessions | where session.utm_source = "google" | last 30d | count by day
sessions | last 30d | count by session.utm_source
sessions | last 30d | count by session.referring_domain | top 10
sessions | last 7d | count by session.landing_path | top 20

Lifecycle & stickiness

lifecycle page_view | last 12w | by week
stickiness page_view | last 30d

Users & B2B

users | where email_domain = "acme.org" | list
user "alice@acme.org" | last 90d | list
* | where user.email_domain = "acme.org" | last 30d | count by event_type
* | where user.plan = "enterprise" | last 12w | count by week

Pages & content

page_view | last 7d | count by _path | top 20
page_view | last 30d | count by _path_clean | top 20
page_view | where _path = "/pricing" | last 30d | count by day
page_view | last 7d | count by _country | top 10

Formulas

formula count(purchase) / count(signup) | last 30d

Advanced

purchase | where user_last_session.region = "DE" | last 30d | sum event_properties.amount
"landing:cta_click" | last 7d | count

Building a Custom Client

Only needed for languages without an official SDK (Go, Python, TypeScript, and browser are covered).

Safety Requirements

  • track() must be non-blocking. Buffer events in memory, send asynchronously via background thread/goroutine/worker. Never make HTTP calls on the caller’s thread.
  • Bounded queue. Cap at ~10,000 events. When full, drop events rather than blocking. Report via optional on_error callback.
  • Graceful shutdown. close() flushes remaining events with a timeout (~10s). Idempotent.
  • Error isolation. All HTTP/network errors caught internally. Never let analytics errors propagate.
  • Thread-safe. Safe for concurrent use from multiple threads/goroutines/tasks.

Batching & Retry

  • Queue events, flush as { "events": [...] } at 10 events or every 2 seconds.
  • Retry 429 and 5xx with exponential backoff (1s, 2s, 4s), max 3 attempts. Drop after max retries.
  • Do NOT retry 4xx (except 429).

Checklist

  1. track(event_type, props?, user_props?) — non-blocking, enqueues and returns immediately
  2. identify(user_id, user_props?, user_prop_ops?) — rejects empty user_id
  3. flush() — blocks until queue drained
  4. close() — flush + stop background worker, idempotent
  5. Auto-generate insert_id (UUID/random hex) for deduplication
  6. Auto-set time to current ISO 8601 UTC
  7. Auto-set library field (e.g. wirelog-ruby/0.1.0)
  8. Config via constructor AND env vars (WIRELOG_API_KEY, WIRELOG_HOST)
  9. disabled mode for test environments (track = no-op)
  10. Optional on_error callback for background errors
  11. For browser clients: persist device_id (localStorage), manage session_id (30-min timeout), set clientOriginated: true, use sendBeacon on page unload