Skip to main content

Analytics

  • Sells as: the analytics product (a product-scoped API key). This capability has no separate product-face contract — its public product route reuses the capability contract below; see the Edge route table's Product face row.

Shared product-tier analytics capability. Ingests generic analytics events/metrics (native, or a platform-event / voice-metric sibling envelope) into one org-scoped store, and serves: aggregated summary + trends + heatmap; the derived-compute crown jewels forecast (linreg+seasonal+80%CI+R²), anomaly detection (z-score 4-band + pattern-change), cost-simulation (±20/50%), tier-recommendation (cheapest tier, cycle-gated); cohorts + retention; funnels + conversion; a predictive-model store; CSV/JSON export; GDPR erase-by-consent; and an org-scoped realtime SSE stream. Forecast/anomaly/tier params + the tier catalog + cost split are config-driven (a product seeds its own economics; voice's hardcoded avgCostPerMinute + 70/30 split are parameterized).

  • Group: Data & analytics
  • Contract: contracts/analytics/v1/openapi.yaml
  • Console: Manage the analytics product in the console →
  • Runbook: operational checklist for analytics — internal (Vagary Labs ops; not part of this public site): docs/runbooks/capability-operations.md#analytics
  • Public base: https://api.vagarylabs.com (the consolidated API gateway — one host, per-brand sibling api.<zone>)
  • Auth: a product API key (vgk_…) issued from the console — Authorization: Bearer vgk_…
  • Product face (customer-keyed):
    • https://api.vagarylabs.com/product/v1/analytics/events

Edge route table​

The routes this capability actually serves on the public edge — live-synced from GET https://api.vagarylabs.com/product/v1/_meta/catalog (snapshot v1). The metric is the request-granularity billing counter; the scope is the key permission required.

Product face (customer-keyed)​

MethodPathMetricScope
POST/product/v1/analytics/eventsanalytics_eventswrite

Endpoints​

MethodPathSummary
POST/v1/analytics/eventsIngest an analytics event. Accepts the NATIVE event shape, a platform-event envelope ({source:platform-event, event_type, entity_id, user_id, properties}), or a voice-metric envelope ({source:voice-metric, event_type:call_start
GET/v1/analytics/eventsNewest-first event feed (org-scoped), optionally filtered by event_type / user_id.
DELETE/v1/analytics/eventsGDPR Art-17 right-to-be-forgotten — erase a DATA SUBJECT's WHOLE analytics footprint within the org, keyed source-AGNOSTICALLY on user_id and/or consent_ref (so BOTH the live 'platform-event' bucket and the migrated 'platform_news_events' bucket are erased). FIRST-PARTY admin only (trusted-service token; an end-user JWT cannot erase). FAIL-CLOSED — at least one selector is REQUIRED (a blank selector is refused, never a blanket wipe). entity_id is NOT a valid selector (not person-identifying). dry_run=true previews the per-bucket footprint WITHOUT deleting. Selectors accepted as query params or JSON body.
GET/v1/analytics/subject-footprintGDPR Art-15 access preview + the 2-bucket correspondence probe — per-source (bucket) event count for a data subject (user_id and/or consent_ref) within the org, NON-mutating. Proves a subject's user_id spans BOTH the 'platform-event' and 'platform_news_events' buckets (the precondition for a source-agnostic erase). FIRST-PARTY admin only. FAIL-CLOSED (>= one selector).
GET/v1/analytics/summaryAggregated dashboard summary (event_count, unique_users, primary/cost totals, by_type). Window defaults to 30 days.
GET/v1/analytics/trendsDaily measure series + first/second-half comparison.
GET/v1/analytics/heatmap7×24 DOW×hour event-count heatmap + peak hour/day (subsumes JS /usage-heatmap + call /peak-hours).
GET/v1/analytics/forecastUsage/cost forecast (least-squares linear regression + weekday/weekend seasonal multiplier + R² + 80% confidence interval + trend) — ports the voice JS forecaster. Needs >= min data points (default 7).
GET/v1/analytics/anomaliesDetect usage anomalies (z-score, 4 severity bands + pattern-change) — ports the voice JS anomaly-detector. list_persisted=true returns stored anomalies (resolved filter); persist=true saves detected ones.
POST/v1/analytics/anomalies/&#123;anomaly_id&#125;/resolveMark a persisted anomaly resolved (org-scoped).
GET/v1/analytics/cost-simulationCost across usage scenarios (Current/±20/±50) × the configured tier catalog — ports tier-recommender simulateCosts. Defaults to the forecasted usage when units omitted. Returns no_tier_catalog when unset.
GET/v1/analytics/tier-recommendationRecommend the cheapest tier for the forecasted usage + savings + should-change gate (min-savings + days-into-cycle) — ports tier-recommender. Returns no_tier_catalog when unset.
GET/v1/analytics/costCost breakdown by the configured component split (parameterized from voice's 70/30 LLM/voice).
GET/v1/analytics/cohortsList cohorts (org-scoped).
POST/v1/analytics/cohortsCreate a cohort (name + criteria_type + criteria).
GET/v1/analytics/cohorts/&#123;cohort_id&#125;Get one cohort.
DELETE/v1/analytics/cohorts/&#123;cohort_id&#125;Delete a cohort.
GET/v1/analytics/cohorts/&#123;cohort_id&#125;/membersResolved cohort membership (user_ids) over the event ledger.
GET/v1/analytics/cohorts/&#123;cohort_id&#125;/retentionCohort retention (D1/D7/D30 by default) — ports get_retention_analysis.
GET/v1/analytics/funnelsList funnels (org-scoped).
POST/v1/analytics/funnelsCreate a funnel (name + steps[]).
GET/v1/analytics/funnels/&#123;funnel_id&#125;Get one funnel.
DELETE/v1/analytics/funnels/&#123;funnel_id&#125;Delete a funnel.
POST/v1/analytics/funnels/&#123;funnel_id&#125;/trackRecord a funnel step occurrence (dedup first-occurrence-wins per org/funnel/step/user).
GET/v1/analytics/funnels/&#123;funnel_id&#125;/conversionPer-step conversion + drop-off over the ordered funnel.
GET/v1/analytics/modelsList predictive models (org-scoped).
POST/v1/analytics/modelsUpsert a predictive model (backs the platform's in-memory model store with persistence).
GET/v1/analytics/models/&#123;model_id&#125;Get one predictive model.
GET/v1/analytics/exportExport events as JSON or CSV (org-scoped) — ports the voice sealed export surface.
POST/v1/analytics/erase-by-consentGDPR Art-17 erasure — delete every event tagged (organization_id, consent_ref). FIRST-PARTY admin only (requires the trusted-service token; an end-user JWT cannot erase). Ports the voice sealed erase-by-consent.
GET/v1/analytics/streamOrg-scoped realtime SSE stream of ingested events (text/event-stream). Subsumes the voice sealed Socket.IO realtime-metric-stream capability at the capability tier (in-process fan-out; the Redis-Sentinel HA transport substrate is a deploy-scale concern, deliberately simplified here).
GET/v1/analytics/reports/customerPer-customer engagement + usage report for the caller's org over a rolling window
POST/v1/funnel-eventsIngest a FLEET-scoped pre-org signup funnel event from the console. Unlike every other route here this one is NOT org-scoped: an attempt_id is minted before any user or organization exists, so there is no tenant to resolve and no organization_id is stored. It therefore accepts the first-party service token ONLY — never an end-user identity JWT, which would let any authenticated tenant write fleet rows. Idempotent on (attempt_id, type); one attempt legitimately emits both submitted and failed.
POST/v1/journey-eventsIngest a batch of JOURNEY events for a registered journey-event contract. Like /v1/funnel-events and unlike every org-scoped route here, this is not tenant-scoped: most of a journey happens before an organization exists, so journey_id is the spine and the first-party service token is the only accepted credential. The accepted contract ids, event names, envelope fields and forbidden keys are NOT defined here — they come from the contract documents the service has registered, which is what lets a second product join the stream without a change to this capability. Dedup is on the emitter-minted event_id, so at-least-once delivery is safe.
GET/v1/journey-funnels/&#123;funnel_id&#125;Stage counts for a registered funnel, in the funnel definition's declared order. Counts are DISTINCT journeys per stage, never rows — an impression event fires once per card, so a row count would make the top of the funnel a function of page layout. A stage with no events is returned with 0 rather than omitted.
GET/v1/journeys/&#123;journey_id&#125;one journey's events, oldest first — the read behind attribution continuity
GET/healthliveness + store/tenancy posture
GET/metricsPrometheus exposition

Schemas​

JourneyEventBatch​

A batch for ONE registered contract. All-or-nothing: one invalid event rejects the batch with a code naming what was wrong, because partial acceptance leaves the emitter unable to tell which of its events landed and produces a funnel nobody downstream can detect is short.

FieldTypeDescription
contract_idstringthe contract this batch is written against; must be registered with the service
eventsarray

JourneyEventReceipt​

FieldTypeDescription
successboolean
acceptedintegerevents in the batch
storedintegerevents that were new
duplicateintegerevents already present under the same event_id; a replay, not a loss

FleetFunnelEventCreate​

The COMPLETE wire body — there is deliberately no properties/metadata field and no identity field of any kind. A pre-org signup attempt has no tenant and no subject, and the receiving table has no column an arbitrary key could land in. That is what makes "product analytics never sees secrets" structural rather than a convention.

FieldTypeDescription
typestring
attempt_idstringa random per-attempt correlation id, derived from nothing (no user, no email, no org)
reasonstringpresent only on signup.failed; a closed code, never the upstream response body
occurred_atstring

EventCreate​

A native analytics event, or a platform-event / voice-metric sibling envelope (discriminated by source). Numeric measures live in measures (native/platform) or are lifted from the typed voice-metric fields.

FieldTypeDescription
organization_idstringend-customer tenant (I6); resolved/verified server-side, never trusted raw
sourcestringnative
event_typestringthe per-product taxonomy (view/click/share OR call_start/latency/cost ...)
entity_idstringarticle/session subject
user_idstring
bot_idstring
site_idstring
consent_refstringGDPR Art-17 erasure key (the voice sealed dim)
measuresobjectnumeric measures (minutes/cost/duration_ms/latency_ms/...)
propertiesobjectplatform-event envelope: raw properties (numerics lifted into measures)
metadataobject
event_idstringidempotency key within (organization_id, source); server-generated if omitted

RecordResult​

FieldTypeDescription
successboolean
event_idstring

Summary​

FieldTypeDescription
successboolean
organization_idstring
user_idstring
bot_idstring
event_countinteger
unique_usersinteger
primary_measurestring
primary_totalnumber
cost_totalnumber
by_typeobject
period_secondsinteger

Forecast​

FieldTypeDescription
successboolean
reasonstring
forecastobject
patternsobject
accuracyobject

EventFeed​

FieldTypeDescription
successboolean
countinteger
eventsarray

Generated by scripts/gen-capability-docs.py from contracts/analytics/v1/openapi.yaml — the contract IS the source of truth; edit the contract, not this page. The Edge route table section is synced from the live edge catalog snapshot (v1) — re-sync with python3 scripts/sync-edge-catalog.py.