Skip to content

API Reference

View as markdown

All requests and responses are JSON unless noted. API keys use X-API-Key; only public pk_ tracking keys may use the key query parameter. Browser project APIs use the wl_session cookie.

Public Endpoints

No authentication required.

MethodPathDescription
GET/Landing page
GET/loginLogin page
GET/auth/githubGitHub OAuth initiation
GET/auth/github/cbGitHub OAuth callback
POST/auth/logoutDestroy session
GET/public/*Static assets (Script Tag, CSS)
GET/llms.txtLLM-readable site summary

API Key Endpoints

POST /track

Ingest events. Single or batch.

Auth: pk_, sk_, or aat_ with track scope.

Single event:

{
"event_type": "page_view",
"user_id": "u123",
"device_id": "d456",
"session_id": "s789",
"time": "2026-01-15T10:30:00Z",
"event_properties": {"page": "/home"},
"user_properties": {"plan": "pro"},
"insert_id": "optional-dedup-id"
}

Batch:

{
"events": [
{"event_type": "click", "event_properties": {"button": "signup"}},
{"event_type": "page_view", "event_properties": {"page": "/pricing"}}
]
}

Response:

{"accepted": 2}

Invalid events in a batch are silently skipped.


POST /identify

Bind a device to a user. Update user profile properties.

Auth: pk_, sk_, or aat_ with track scope.

{
"user_id": "alice@acme.org",
"device_id": "dev_123",
"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}

POST /query

Run a pipe DSL query.

Auth: sk_ or aat_ with query scope.

{
"q": "page_view | where _path in [\"/\",\"/pricing\",\"/docs\"] | last 7d | count by _browser",
"format": "llm",
"limit": 100,
"offset": 0
}
FieldRequiredDefaultNotes
qYes—Pipe DSL query string
formatNo"llm""llm" (Markdown), "json", or "csv"
limitNo100Max 10,000
offsetNo0Pagination offset

Response: Markdown table, JSON array, or CSV depending on format.

For format: "json" aggregate-style rows, canonical metric fields are included:

  • value: numeric metric output
  • metric: metric identifier (count, unique, sum, avg, etc.)

Legacy metric keys (count, unique_count, total, …) are preserved for compatibility. rows_scanned reports ClickHouse source rows read while executing the query, not result rows returned.

Mode-specific metadata is additive and optional. Funnel JSON responses include:

{
"mode": "funnel",
"metadata": {
"funnel_steps": ["landing", "signup", "activate"]
}
}

PUT /api/dashboard-sync/{dashboardKey}

Sync validated dashboard YAML into the API credential’s project. The route accepts no project ID; tenant scope comes only from the authenticated key.

Omitted visibility defaults from the credential only when creating a dashboard. Re-sync preserves existing visibility unless personal or project is sent explicitly.

Auth: sk_ or aat_ with dashboards scope. pk_ and ak_ are rejected.

{
"source_yaml": "version: 1\nid: product-growth\ntitle: Product Growth\n...",
"visibility": "personal"
}

The server strictly validates YAML and stores immutable content versions. The response includes changed for a new version and metadata_changed for a visibility-only update.


Session-Authenticated API

Session routes require the wl_session cookie (set by GitHub OAuth login). Missing sessions return JSON 401, and unsafe cross-origin session requests are denied.

Organizations

GET /api/orgs — List user’s orgs.

POST /api/orgs — Create org.

{"name": "Acme Corp"}

GET /api/orgs/{orgID} — Get org details.

POST /api/orgs/{orgID}/rotate-admin-key — Owner only. Rotate the org admin key (ak_).

{"admin_key": "ak_..."}

Members, Invitations, and Domains

Owners can manage team access:

  • GET /api/orgs/{orgID}/members
  • PATCH /api/orgs/{orgID}/members/{userID} with {"role":"owner|admin|member"}
  • DELETE /api/orgs/{orgID}/members/{userID}
  • POST /api/orgs/{orgID}/invitations with {"email":"person@example.com","role":"member|admin"}
  • POST /api/invitations/{token}/accept
  • POST /api/orgs/{orgID}/domains with {"domain":"example.com"}
  • POST /api/orgs/{orgID}/domains/{domainID}/verify

Domain verification expects TXT _wirelog-verify.<domain> with value wirelog-verification=<token>.


Projects

GET /api/orgs/{orgID}/projects — List org projects.

POST /api/orgs/{orgID}/projects — Admin/owner. Create project.

{"name": "My App"}

Returns project with public_key (pk_). secret_key (sk_) is omitted for members.

GET /api/projects/{projectID} — Get project. Includes role/capabilities; secret_key is omitted for members.

POST /api/projects/{projectID}/query — Session-authenticated query route for members/admins/owners.

GET /api/projects/{projectID}/dashboards — List shared dashboards plus the current user’s personal dashboards.

GET /api/projects/{projectID}/dashboards/{dashboardID} — Get one visible dashboard’s validated current config.

DELETE /api/projects/{projectID} — Owner only. Delete project permanently.

{
"project_name": "My App",
"project_name_confirm": "My App"
}

Both fields must match exactly. Deletes project and all associated data.

POST /api/projects/{projectID}/rotate-secret-key — Admin/owner. Rotate sk_ key. Public key unchanged.


Access Tokens

POST /api/projects/{projectID}/tokens — Create scoped access token.

{
"name": "cli-dashboard",
"kind": "personal",
"scopes": ["query", "dashboards"],
"expires_in": "720h"
}

Members can create personal tokens with query and/or dashboards. Admins/owners can create service tokens; owner role is required for admin scope. Returns the raw token once. It cannot be retrieved again.

GET /api/projects/{projectID}/tokens — Members see their own token metadata. Admins/owners see project token metadata.

DELETE /api/projects/{projectID}/tokens/{tokenID} — Revoke own token, or any project token as admin/owner.

GET /api/me/tokens — List your personal tokens.

DELETE /api/me/tokens/{tokenID} — Revoke your personal token.


Choices

Choices use the normal ingest and query APIs. SDKs emit wirelog.exposure events through /track; results are queried through /query.

{"q":"choice landing_h1 | results signup | window 7d | unit user_id","format":"json"}

Choice result packets include choice_key, conversion_event, variants, comparisons, diagnostics, warnings, decision_status, method, and generated_at.


Usage

GET /api/projects/{projectID}/usage — Daily usage stats.

Query params: ?from=YYYY-MM-DD&to=YYYY-MM-DD. Defaults to current month.


Admin API

Auth: org admin key (ak_) via X-API-Key header. All routes under /api/admin/*.

MethodPathDescription
GET/api/admin/projectsList org projects
POST/api/admin/projectsCreate project
GET/api/admin/projects/{projectID}Get project (includes keys)
DELETE/api/admin/projects/{projectID}Delete project (same confirmation body as session API)
POST/api/admin/projects/{projectID}/rotate-secret-keyRotate sk_ key
GET/api/admin/projects/{projectID}/usageDaily usage stats

GDPR

Auth: sk_ or aat_ with admin scope via X-API-Key header.

DELETE /api/gdpr/users/{userID} — Delete all user data: events, identity mappings, and profile rows. Logged to audit trail.

GET /api/gdpr/users/{userID}/export — Stream all user events as NDJSON. Logged to audit trail.


API Key Types

PrefixTypeScopesClient-safe?
pk_Public keytrackYes
sk_Secret keytrack, query, adminNo
ak_Admin keyorg adminNo
aat_Access tokencustom per-tokenDepends on scopes
  • pk_ keys are safe to expose in client-side code. They can ingest events.
  • sk_ keys have full project access. Never expose in browsers or public repos.
  • ak_ keys have org-level admin access. Never expose in client code.
  • aat_ tokens are hashed before storage. The raw token is shown once at creation.

Error Format

All errors return:

{"error": "message"}
CodeMeaning
400Bad request
401Missing or invalid authentication
403Insufficient scope or limit reached
404Not found
413Payload too large
429Rate limited (includes Retry-After: 60 header)
503Feature disabled
504Query timeout