Skip to content

Tour Analytics ​

The analytics panel in the admin overlay provides real-time visibility into how users engage with tours and content. It surfaces completion rates, step-level engagement, drop-off analysis, and selector health metrics.

Data Architecture ​

Analytics data is stored in two DynamoDB tables using a two-tier rollup strategy:

Events.Interactions (Raw Events) ​

Append-only log of individual user interaction events. Each event has a 90-day TTL.

PK: tenant_id#content_id     (e.g. "tenant_abc#tour_123")
SK: timestamp#event_id       (e.g. "2025-05-10T14:22:00Z#uuid")

Events.Aggregates (Pre-computed Counters) ​

Hourly and daily counters maintained atomically on every interaction:

PK: tenant_id#content_id     (e.g. "tenant_abc#tour_123")
SK: date_metric              (e.g. "2025-05-10#views" or "2025-05-10T14#completions")

The sort key format determines granularity:

  • Daily: YYYY-MM-DD#metric -- used for trend analysis (weekly/monthly views).
  • Hourly: YYYY-MM-DDTHH#metric -- used for fine-grained dashboards (last-24-hour view).

Both counters are incremented atomically on every interaction using DynamoDB ADD operations -- no batch jobs or delayed processing.

Tracked Metrics ​

The following events are captured and aggregated:

Tour Events ​

EventMetric NameDescription
TourStartedstartsUser began the tour
StepViewedstep_viewsUser saw a specific step
StepCompletedstep_completionsUser advanced past a step
TourCompletedcompletionsUser finished all steps
TourDismisseddismissalsUser closed the tour early
TourRestartedrestartsUser restarted the tour

Content Events ​

EventMetric NameDescription
ContentViewedviewsUser saw the content
ContentDismisseddismissalsUser dismissed the content
ContentCtaClickedcta_clicksUser clicked the call-to-action

Targeting Events ​

EventMetric NameDescription
target_not_foundtarget_failuresSDK could not locate the target element

Completion Rate ​

The primary metric for tour effectiveness. Calculated as:

completion_rate = completions / starts * 100

The analytics panel displays this as a percentage with a trend indicator showing change over the selected time period.

Drop-off Analysis ​

The step-level funnel view shows where users abandon tours:

Step 1: "Welcome"          ████████████████████  100% (200 users)
Step 2: "Navigate to..."   ██████████████████    90%  (180 users)
Step 3: "Click the..."     ████████████████      80%  (160 users)
Step 4: "Configure..."     █████████             45%  (90 users)  <-- drop-off
Step 5: "Complete"         ███████               35%  (70 users)

The step with the largest percentage drop is highlighted to identify where the tour needs improvement. Common causes:

  • The target element is not visible (lazy-loaded, behind a tab, requires scrolling).
  • The step instructions are unclear.
  • The step requires an action the user is not ready to take.

Querying Analytics ​

Daily Aggregates ​

bash
curl -H "X-API-Key: sk_live_..." \
     -H "Authorization: Bearer <jwt>" \
     "https://api.example.com/v1/admin/analytics/tours/tour_123?date_from=2025-05-01&date_to=2025-05-10"
json
{
  "data": {
    "tour_id": "tour_123",
    "period": {
      "from": "2025-05-01",
      "to": "2025-05-10"
    },
    "totals": {
      "starts": 1250,
      "completions": 875,
      "dismissals": 312,
      "completion_rate": 70.0
    },
    "daily": [
      {
        "date": "2025-05-01",
        "starts": 150,
        "completions": 105,
        "dismissals": 38,
        "completion_rate": 70.0
      }
    ]
  }
}

Hourly Aggregates ​

For fine-grained views (last 24 hours):

bash
curl -H "X-API-Key: sk_live_..." \
     -H "Authorization: Bearer <jwt>" \
     "https://api.example.com/v1/admin/analytics/tours/tour_123?date=2025-05-10&granularity=hourly"
json
{
  "data": {
    "tour_id": "tour_123",
    "date": "2025-05-10",
    "granularity": "hourly",
    "hours": [
      { "hour": "00", "starts": 2, "completions": 1, "dismissals": 1 },
      { "hour": "01", "starts": 0, "completions": 0, "dismissals": 0 },
      { "hour": "09", "starts": 45, "completions": 32, "dismissals": 10 },
      { "hour": "10", "starts": 62, "completions": 44, "dismissals": 15 }
    ]
  }
}

Activity Feed ​

The Activity.UserEvents table stores a per-user event log with three access patterns:

GSIQuestion AnsweredKey Structure
Primary key"What has this user been doing?"PK: tenant_user_id, SK: timestamp_event_id
gsi-content"Who interacted with this tour?"PK: tenant_content_id, SK: timestamp
gsi-session"What happened in this session?"PK: tenant_session_id, SK: timestamp

Activity events have a configurable TTL (default: 90 days) to manage storage costs.

Admin Panel Views ​

The analytics panel in the admin overlay provides four views:

Overview Dashboard ​

Aggregate metrics across all tours and content for the tenant:

  • Total active tours and content items
  • Combined completion rate with trend
  • Top-performing tour (highest completion rate)
  • Tour with highest drop-off (needs attention)

Tour Detail View ​

Per-tour metrics with step-level funnel:

  • Completion rate with daily trend chart
  • Step-by-step funnel with drop-off markers
  • User count by status (completed, in-progress, dismissed)
  • Recent activity feed

Selector Health View ​

Aggregated targeting failure report (see Element Selector):

  • Total failures in the analysis window
  • Broken selectors ranked by failure count
  • Drill-down to individual failure events per content item

Export ​

Analytics data can be exported via the admin API for integration with external BI tools. The raw Events.Interactions table data is queryable with date range filters.

Event Processing Pipeline ​

User Interaction (SDK)
        |
        v
POST /v1/sdk/events
        |
        v
Events.Outbox table (transactional write)
        |
        v
Outbox Processor (polls pending events)
        |
        v
EventBridge (spotlight-events bus)
        |
        +---> Activity Handler --> Activity.UserEvents
        |
        +---> Analytics Handler --> Events.Interactions
        |                          Events.Aggregates
        |
        +---> Audit Handler --> Audit.AdminActions (admin events only)

In local development, the outbox processor runs as a FastAPI background task (configured via ENABLE_LOCAL_PROCESSOR=true) instead of a separate Lambda function, polling every LOCAL_PROCESSOR_INTERVAL seconds.

In production, DynamoDB Streams on the outbox table trigger a Lambda function that publishes events to EventBridge, which routes them to the stream processor, analytics processor, and any configured webhook targets.

Spotlight