Skip to main content

Analytics: a view

Analytics is reading user state. Funnels, retention, and cohorts are queries over the same live projection the rest of the engine acts on.

At a glance

Host
https://eu.api.prodantix.com
Ingest
POST /v1/events and its rules: Ingest API
Query
analytics and events queries: GraphQL reference
  • Funnels and retention computed on live state, not a nightly warehouse rollup
  • Cohorts defined once and kept current as events arrive
  • No SQL round-trip, the state is already shaped for the question

How it works

Events land on the EU ingestion host, are buffered through Redpanda and written to ClickHouse. Queries read from ClickHouse, so a funnel over thirty days answers in the same shape whether it spans a thousand events or a billion.

Ingestion answers 202: acceptance for processing, not proof of storage. The Ingest API page carries the full contract.

Where to reach it

SurfaceOperation
RESTPOST /v1/events · POST /v1/identify · POST /v1/group
GraphQLrunQuery · runFunnel · runRetention · runPaths · runStickiness · runLifecycle · events · dashboards, cohorts
MCPprodantix.analytics.runQuery

Example

curl -X POST "https://eu.api.prodantix.com/v1/events" \
  -H "Authorization: Bearer $PRODANTIX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "distinct_id": "u_8f3a",
      "event_id": "5f6b2c1e-8f4b-4c6e-9d2a-7b1e3c4d5a6f",
      "event_name": "order.completed",
      "properties": { "amount": 4200 },
      "schema_version": 1,
      "timestamp": "2026-08-31T12:00:00.000Z"
    }],
    "sent_at": "2026-08-31T12:00:01.000Z"
  }'

Querying a trend

A trend takes one or more metrics, each its own event and measure, an optional formula per letter, up to three breakdowns and a comparison against an earlier window. A single metric skips the array: event, measure and, for one breakdown, breakdown stand in for metrics and breakdowns, and the two shapes never mix in the same query.

GraphQL
query RunQuery($projectId: String!, $query: MetricsQueryInput!) {
  runQuery(projectId: $projectId, query: $query) {
    series {
      key
      metric
      breakdown
      past
      points {
        time
        value
      }
    }
  }
}

# $query
{
  "metrics": [
    { "event": "checkout.started", "measure": { "kind": "total" } },
    { "event": "checkout.completed", "measure": { "kind": "total" } }
  ],
  "formulas": [{ "expression": "B / A * 100" }],
  "breakdowns": [{ "property": "plan" }, { "column": "geo_country" }],
  "compare": { "to": "previous_period" },
  "dateRange": { "from": "2026-08-01T00:00:00Z", "to": "2026-09-01T00:00:00Z" },
  "granularity": "day"
}

Querying a funnel

GraphQL
query Funnel($projectId: String!, $query: JSON!) {
  runFunnel(projectId: $projectId, query: $query) {
    step
    event
    users
    conversionFromPrevious
    droppedOff
  }
}

# $query
{
  "steps": [{ "event": "signup.started" }, { "event": "signup.completed" }, { "event": "order.completed" }],
  "conversionWindowSeconds": 2592000,
  "dateRange": { "from": "2026-08-01T00:00:00Z", "to": "2026-09-01T00:00:00Z" }
}

Paths, stickiness and lifecycle

Three more reports take the same shape: a JSON query the server validates against the report's own parser, and a list of rows back. Paths walk one to five steps after (or before) an anchor and rank the top three, five or ten events per step; stickiness counts a period's users by their active days; lifecycle sorts a period's users into new, returning, resurrecting and dormant.

GraphQL
query Paths($projectId: String!, $query: JSON!) {
  runPaths(projectId: $projectId, query: $query) {
    step
    users
    events { event users share }
    otherEvents
    otherUsers
    droppedOff
  }
}

# $query
{
  "anchorEvent": "checkout.started",
  "direction": "after",
  "steps": 3,
  "topN": 5,
  "dateRange": { "from": "2026-08-01T00:00:00Z", "to": "2026-09-01T00:00:00Z" }
}
GraphQL
query Stickiness($projectId: String!, $query: JSON!) {
  runStickiness(projectId: $projectId, query: $query) {
    period
    users
    days { daysActive users }
  }
}

# $query
{
  "event": "session.started",
  "bucket": "week",
  "dateRange": { "from": "2026-07-06T00:00:00Z", "to": "2026-09-01T00:00:00Z" }
}
GraphQL
query Lifecycle($projectId: String!, $query: JSON!) {
  runLifecycle(projectId: $projectId, query: $query) {
    period
    new
    returning
    resurrecting
    dormant
  }
}

# $query
{
  "event": "session.started",
  "bucket": "week",
  "dateRange": { "from": "2026-07-27T00:00:00Z", "to": "2026-09-07T00:00:00Z" }
}