Quickstart
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
| Prefix | Name | Permissions | Where to use |
|---|---|---|---|
pk_ | Public | Track only | Client-side (browsers, mobile, agents) |
sk_ | Secret | Track + query + identify | Server-side only — never expose |
aat_ | Access token | Scoped (track/query/admin), time-limited | Agents, 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.
| Attribute | Description |
|---|---|
data-key | Public key (required) |
data-host | API 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 eventwl.identify("user_id", { name: "Alice" }) // bind device to user + set profilewl.reset() // clear identity (logout)wl.optIn() / wl.optOut() // consent managementwl.flush() // manual flushIdentity: 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
npm install wirelogimport { 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 shutdownZero 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
pip install wirelogfrom 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 shutdownZero external dependencies. Python 3.9+. Background thread batches 10 events or every 2s with retry. Context manager supported: with WireLog() as wl:.
Go
go get github.com/wirelogai/wirelog-goimport 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
brew install wirelogai/tap/wl# or: go install github.com/wirelogai/wirelog-cli@latestwl config init # interactive setupwl inspect # discover eventswl query "* | last 7d | count by event_type" # querywl track page_view --user-id u1 --prop path=/home # trackwl identify --user-id u1 --prop plan=pro # identifyAuto-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)
# Trackcurl -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"}'
# Identifycurl -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_propertiesfor event data,user_properties/identify for user data, senddevice_idon 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 | ...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 propertiesinspect signup | last 7d -- single event property detail* | last 30d | count by event_type | top 20 -- lightweight event countsfields | last 7d -- all available fieldsSources
| Source | Syntax | Description |
|---|---|---|
| Event | page_view, "landing:cta_click", * | Events by name. * = all. Quotes for special chars. |
| Funnel | funnel a -> b -> c | Conversion funnel. Optional: exclude x, y (between-step). |
| Retention | retention signup | Week-over-week cohort. Optional: returning <event>. |
| Paths | paths from signup or paths to purchase | Event sequences. Use | by _path for URL nav flows. |
| Sessions | sessions | Session-level analysis. |
| Lifecycle | lifecycle page_view | New / Returning / Resurrected / Dormant segments. |
| Stickiness | stickiness page_view | Days active per period distribution. |
| User timeline | user "alice@acme.org" | Single user event history. |
| Users directory | users | User profile listing. |
| Formula | formula count(a) / count(b) | Ratios. Supports: count, unique, sum, avg. |
| Fields | fields | Available field names including dynamic properties. |
| Inspect | inspect * or inspect signup | Schema discovery with coverage %, types, samples. |
Stages
| Stage | Syntax |
|---|---|
| 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 30dinspect signup | last 7d* | last 30d | count by event_type | top 20fields | last 7dCounts & trends
signup | last 7d | countsignup | last 30d | count by daysignup | last 90d | unique distinct_id by weekpage_view | where _platform = "web" | last 30d | count* | last 24h | count by event_typeFunnels
funnel signup -> activate -> purchase | last 30dfunnel signup -> activate -> purchase exclude support_ticket | window 7dfunnel signup -> activate -> purchase | last 30d | by _platformRetention
retention signup | last 90dretention signup returning core_usage | last 90dPaths
paths from signup | window 7d | last 30d | depth 8paths from page_view | last 30d | by _pathpaths from page_view | last 30d | by _path_cleanSessions & attribution
sessions | where session.utm_source = "google" | last 30d | count by daysessions | last 30d | count by session.utm_sourcesessions | last 30d | count by session.referring_domain | top 10sessions | last 7d | count by session.landing_path | top 20Lifecycle & stickiness
lifecycle page_view | last 12w | by weekstickiness page_view | last 30dUsers & B2B
users | where email_domain = "acme.org" | listuser "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 weekPages & content
page_view | last 7d | count by _path | top 20page_view | last 30d | count by _path_clean | top 20page_view | where _path = "/pricing" | last 30d | count by daypage_view | last 7d | count by _country | top 10Formulas
formula count(purchase) / count(signup) | last 30dAdvanced
purchase | where user_last_session.region = "DE" | last 30d | sum event_properties.amount"landing:cta_click" | last 7d | countBuilding 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_errorcallback. - 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
track(event_type, props?, user_props?)— non-blocking, enqueues and returns immediatelyidentify(user_id, user_props?, user_prop_ops?)— rejects empty user_idflush()— blocks until queue drainedclose()— flush + stop background worker, idempotent- Auto-generate
insert_id(UUID/random hex) for deduplication - Auto-set
timeto current ISO 8601 UTC - Auto-set
libraryfield (e.g.wirelog-ruby/0.1.0) - Config via constructor AND env vars (
WIRELOG_API_KEY,WIRELOG_HOST) disabledmode for test environments (track = no-op)- Optional
on_errorcallback for background errors - For browser clients: persist device_id (localStorage), manage session_id (30-min timeout), set
clientOriginated: true, use sendBeacon on page unload