Customer Data API — Developer Documentation

Overview

The Walnut Customer Data API gives you programmatic, self-service access to your demo analytics data. Query session-level insights, retrieve engagement metrics, and feed data directly into your CRM, BI tools, internal dashboards, or AI-powered workflows.

This initial release exposes demo session data. Additional endpoints (demo metadata, playlist sessions, admin analytics) will be introduced in future releases.

Key Capabilities

  • Query individual demo sessions with filtering, pagination, and field selection
  • Aggregate session data by date, user type, viewer type, demo, or embed status
  • Retrieve pre-built analytics summaries with totals, averages, and top-10 breakdowns
  • Integrate demo data into any system that can make HTTP requests

Authentication

All API requests (except the health check endpoint) require authentication via an API key passed in the x-api-key request header.

Making an Authenticated Request

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?limit=10>"

⚠️ Your API key is provided once by the Walnut team and cannot be recovered if lost. Store it securely. If you need a new key, contact your Walnut account representative.

Rate Limits

The API enforces the following rate limits per API key:

LimitValue
Sustained throughput50 requests/second
Burst allowance100 requests/second
Max rows per request10,000

Requests exceeding the rate limit will receive a 429 Too Many Requests response. Implement exponential backoff in your integration.


Endpoints

PathMethodAuthDescription
/healthGETNoneHealth check
/demo-sessionsGETx-api-keyQuery demo sessions with filtering, pagination, field selection, and aggregation
/demo-sessions/summaryGETx-api-keyPre-built analytics summary (totals, averages, top demos, top countries)

GET /demo-sessions

Retrieve demo session records with support for filtering, pagination, field selection, and aggregation.

Query Parameters

ParameterTypeDefaultDescription
fieldsstring(all fields)Comma-separated list of fields to return
limitinteger1,000Max rows to return (maximum: 10,000)
offsetinteger0Pagination offset
start_dateYYYY-MM-DD(none)Include sessions from this date (inclusive)
end_dateYYYY-MM-DD(none)Include sessions up to this date (inclusive)
user_typestringallFilter by user type: "external", "internal", or "all"
demo_idUUID(none)Filter sessions for a specific demo
group_bystring(none)Aggregate results by: date, user_type, viewer_type, demo_id, is_embed

Available Fields (29)

Use the fields parameter to select only the data you need. If omitted, all fields are returned.

CategoryFields
Identifiersid, demo_id, company_id
Classificationviewer_type, user_type
Timingstarted_at, finished_at
Session Stateis_bounced, is_completed, is_embed, is_offline
Engagementinteraction_count, screen_views_count, annotation_views_count, guide_views_count
Geographygeo_country_code, geo_country_name, geo_city
Contextorigin_url, demo_owner_name, demo_name
Lead Formlead_form_exists, lead_form_opened, lead_form_opened_at, lead_form_skipped, lead_form_skipped_at, lead_form_submitted, lead_form_submitted_at
Gatesemail_gate_exists, access_gate_exists

Aggregation

Pass one or more dimensions to group_by (comma-separated) to receive aggregated counts instead of individual session records. For example, group_by=date,user_type returns session counts grouped by both date and user type.

Response Schema

Standard response (individual sessions):

{
  "data": [
    {
      "id": "7b4ab219-ab64-4ae5-8a96-601237c212c1",
      "demo_id": "bacdb032-c3a2-4a38-9b85-a45a5e3639f8",
      "company_id": "3003886f-9618-46b9-9baf-dcbba56a891a",
      "viewer_type": "prospect",
      "user_type": "external",
      "started_at": "2026-03-08T10:37:25.286Z",
      "finished_at": null,
      "is_bounced": false,
      "is_completed": false,
      "interaction_count": 0,
      "screen_views_count": 1,
      "geo_country_code": "USA",
      "geo_country_name": "United States",
      "geo_city": "Dallas",
      "is_embed": true,
      "is_offline": false,
      "origin_url": "<https://app.teamwalnut.com/player/>...",
      "demo_owner_name": "Jane Smith",
      "lead_form_exists": false,
      "lead_form_opened": false,
      "lead_form_opened_at": null,
      "lead_form_skipped": false,
      "lead_form_skipped_at": null,
      "lead_form_submitted": false,
      "lead_form_submitted_at": null,
      "email_gate_exists": false,
      "access_gate_exists": false,
      "annotation_views_count": 1,
      "guide_views_count": 1,
      "demo_name": "Product Overview Demo"
    }
  ],
  "count": 1,
  "total": 253852,
  "limit": 1,
  "offset": 0
}

count = rows returned in this response page. total = total matching rows across all pages. Use these for pagination logic.

Single-dimension aggregation (group_by=date):

{
  "data": {
    "2026-03-01": 669,
    "2026-03-02": 822,
    "2026-03-03": 471
  },
  "total": 1962,
  "group_by": ["date"]
}

Multi-dimension aggregation (group_by=date,user_type):

{
  "data": {
    "2026-03-01|external": 669,
    "2026-03-02|external": 820,
    "2026-03-02|internal": 2,
    "2026-03-03|external": 471
  },
  "total": 1962,
  "group_by": ["date", "user_type"]
}

Composite keys use the | (pipe) delimiter. Dimensions with zero sessions are omitted from the response — absence means zero.

Sort Order

Results are returned newest-first (descending by started_at). There is currently no parameter to change sort order.


Field Types, Values & Definitions

Every field returned by /demo-sessions is documented below with its data type, possible values, and what it means in the context of Walnut demo analytics.

FieldTypeValues / FormatDefinition
idUUID"7b4ab219-ab64-..."Unique session identifier
demo_idUUID"bacdb032-c3a2-..."The demo that was viewed
company_idUUID"3003886f-9618-..."Your Walnut company/account ID
viewer_typestring"prospect""owner"
user_typestring"external""internal"
started_atISO 8601"2026-03-08T10:37:25.286Z"When the session began (always present)
finished_atISO 8601 or null"2026-03-08T10:42:00.000Z" or nullWhen the session ended. null means the session is still open or was abandoned without a completion event.
is_bouncedbooleantrue / falseThe viewer exited after the first screen with no further interaction. Use bounce rate (bounced / total) to measure first-screen effectiveness and gating friction.
is_completedbooleantrue / falseThe viewer reached the end of the demo flow (viewed all screens). Use completion rate (completed / total) to measure narrative quality and content stickiness.
is_embedbooleantrue / falsetrue = demo was viewed inside an embedded iframe (e.g., on your website). false = viewed via direct link or Walnut player.
is_offlinebooleantrue / falseWhether the session was an offline/downloaded demo view
interaction_countinteger0+Total interactive clicks during the session — includes FAB (Floating Action Button) clicks, annotation clicks, and guide step advances. A proxy for engagement depth.
screen_views_countinteger0+Number of demo screens viewed in the session. Compare to total screens in the demo to calculate screens viewed (%).
annotation_views_countinteger0+Number of annotation overlays (tooltips, modals, highlights on screens) the viewer saw. Annotations are the individual visual elements placed on screens to explain features.
guide_views_countinteger0+Number of guide chapters (not individual steps) viewed. Guides are the structured narrative flows that walk viewers through a demo story. Measured at chapter level, not step level.
geo_country_codestring"USA", "GBR", etc.ISO 3166-1 alpha-3 country code based on viewer IP
geo_country_namestring"United States"Full country name
geo_citystring or null"Dallas" or nullCity based on IP geolocation. null when unavailable.
origin_urlstringFull URLThe URL where the demo was loaded from (Walnut player URL, or the page where the embed lives)
demo_owner_namestringDisplay nameThe Walnut team member who owns/created this demo
demo_namestringDemo titleThe name of the demo as set in the Walnut builder
lead_form_existsbooleantrue / falseWhether this demo has a lead form configured
lead_form_openedbooleantrue / falseWhether the lead form was displayed to the viewer
lead_form_opened_atISO 8601 or nullTimestamp or nullWhen the lead form was displayed
lead_form_skippedbooleantrue / falseWhether the viewer dismissed the form without submitting
lead_form_skipped_atISO 8601 or nullTimestamp or nullWhen the form was skipped
lead_form_submittedbooleantrue / falseWhether the viewer submitted the lead form. Calculate lead form conversion as submitted / opened.
lead_form_submitted_atISO 8601 or nullTimestamp or nullWhen the form was submitted
email_gate_existsbooleantrue / falseWhether this demo requires an email gate (viewer must enter email before accessing the demo)
access_gate_existsbooleantrue / falseWhether this demo has an access gate (password or approval required)

GET /demo-sessions/summary

Returns a pre-built analytics summary with 7 parallel aggregations, giving you a comprehensive snapshot of your demo performance.

Summary Includes

  1. Period — the date range covered (auto-scopes to last 12 months if no date filters provided)
  2. Totals — sessions, completed, bounced
  3. Averages — duration (seconds), interactions, screen views
  4. By user type — count distribution across user types (external, internal)
  5. By viewer type — count distribution across viewer types (prospect, owner, colleague_member, colleague_non_member)
  6. By embed statusembedded vs. direct session counts
  7. Top 10 demos — ranked by session count (includes demo_id, demo_name, sessions)
  8. Top 10 countries — ranked by session count (includes country, sessions)

Optional Filters

The summary endpoint accepts start_date and end_date query parameters to scope the summary to a specific date range. When omitted, defaults to the last 12 months.

Response Schema

{
  "period": {
    "start": "2025-03-08",
    "end": "2026-03-08"
  },
  "totals": {
    "sessions": 253852,
    "completed": 253835,
    "bounced": 218118
  },
  "averages": {
    "duration_seconds": 21,
    "interactions": 0.4,
    "screen_views": 1.7
  },
  "by_user_type": {
    "external": 245083,
    "internal": 8769
  },
  "by_viewer_type": {
    "colleague_member": 5298,
    "colleague_non_member": 811,
    "owner": 2660,
    "prospect": 245083
  },
  "by_embed": {
    "direct": 24589,
    "embedded": 229263
  },
  "top_demos": [
    {
      "demo_id": "4c1e1a49-ba4d-4951-9f6a-6005648e8eea",
      "demo_name": "Product Overview Demo",
      "sessions": 31668
    }
  ],
  "top_countries": [
    {
      "country": "United States",
      "sessions": 107132
    }
  ]
}

Note: averages.duration_seconds is an integer. averages.interactions and averages.screen_views are floats.


Examples

All examples use the production base URL. Replace YOUR_API_KEY with the key provided by your Walnut account team.

Health Check (no authentication required)

curl <https://customer-api.teamwalnut.com/health>

Response:

{ "status": "ok" }

Retrieve Last 10 Sessions

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?limit=10>"

Filter by Date Range and User Type

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?start_date=2025-01-01&end_date=2025-12-31&user_type=external&limit=50>"

Select Specific Fields

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?fields=id,demo_id,started_at,is_completed,user_type&limit=20>"

Aggregate by Date

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?start_date=2025-01-01&group_by=date>"

Multi-Dimensional Aggregation

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?group_by=date,user_type>"

Analytics Summary

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions/summary>"

Summary with Date Filter

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions/summary?start_date=2025-01-01>"

Filter by Specific Demo

curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?demo_id=YOUR_DEMO_UUID&limit=100>"

Pagination

The /demo-sessions endpoint returns up to limit rows per request (default 1,000; maximum 10,000). Use the offset parameter to paginate through larger datasets.

Example: Paginating Through Results

# Page 1 (rows 0-999)
curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?limit=1000&offset=0>"

# Page 2 (rows 1000-1999)
curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?limit=1000&offset=1000>"

# Page 3 (rows 2000-2999)
curl -H "x-api-key: YOUR_API_KEY" \\
  "<https://customer-api.teamwalnut.com/demo-sessions?limit=1000&offset=2000>"

Continue incrementing offset by your limit value until fewer rows than limit are returned, indicating you have reached the end of the dataset.


Error Handling

The API uses standard HTTP status codes. Error responses return a JSON body with a descriptive message.

Status CodeMeaningCommon Cause
200SuccessRequest completed successfully
400Bad RequestInvalid date format, unknown field name, or malformed parameter
401UnauthorizedMissing or invalid API key
429Too Many RequestsRate limit exceeded; retry with exponential backoff
500Internal Server ErrorUnexpected server issue; contact support if persistent

Error response body:

{ "error": "Invalid fields requested" }

ℹ️ Error responses never expose internal details. If you receive a 500 error consistently, contact your Walnut account team with the request URL and timestamp.


What Can You Do With This Data?

The fields returned by the API map directly to actionable metrics. Here are recipes your team can implement immediately.

Key Metrics You Can Calculate

MetricFormulaWhy It Matters
Bounce Rateis_bounced=true count / total sessionsMeasures first-screen effectiveness. High bounce = weak opener or too-early gating.
Completion Rateis_completed=true count / total sessionsMeasures narrative quality. Higher completion = story resonates and holds attention.
Lead Form Conversionlead_form_submitted / lead_form_openedIs your form friction too high? Low conversion suggests the form appears too early or asks too much.
Engagement Depthinteraction_countscreen_views_count per sessionAre prospects exploring or skimming? Higher depth = stronger buying intent.
Internal vs External SplitFilter by user_typeHigh internal usage may mean reps are previewing, not sending to prospects. Track the ratio over time.
Embed PerformanceCompare is_embed=true vs false sessionsAre website embeds converting better than direct links? Optimize placement accordingly.
Identification CoverageSessions with email_gate_exists=true or lead_form_exists=true / totalThe more viewers you can identify, the more complete your attribution picture becomes. Aim for 70–80% coverage.
Top Demos by EngagementSort by avg interaction_count, not just session countVolume ≠ quality. A demo with fewer sessions but high interaction may be your best closer.

Common Integration Patterns

  • CRM sync: Pull sessions daily, match origin_url or lead form data to opportunities in Salesforce/HubSpot. Use demo_name and demo_owner_name for attribution.
  • BI dashboards: Use group_by=date for trend charts, /demo-sessions/summary for executive snapshots. The period object tells you the exact date range covered.
  • Hot lead alerts: Poll for sessions where is_completed=true AND interaction_count > 5 to notify sales of high-intent viewers.
  • Re-engagement triggers: Flag open deals where recent sessions show is_bounced=true — send a shorter, more targeted demo or playlist.
  • Embed optimization: Compare by_embed data in the summary to decide whether to invest in more website embeds or direct-link campaigns.
  • Pagination loop: Increment offset by your limit value until count < limit, indicating you’ve reached the end of the dataset.

Current Limitations

This is the initial release of the Customer Data API. Please note the following current constraints:

  • Read-only access: the API supports data retrieval only; there are no write endpoints.
  • Maximum of 10,000 rows per request. Use pagination for larger datasets.
  • Offset-based pagination only. There is no cursor-based pagination. For very large datasets, offset pagination may slow down at high offsets.
  • API key changes (including revocations) may take up to 5 minutes to take effect due to authorization caching.
  • No self-service key rotation. If you lose your API key, contact your Walnut account representative for a new one.
  • Demo session data only. Additional data types (demo metadata, playlist sessions, admin analytics) will be added in future releases.
  • Fixed sort order. Results are always returned newest-first by started_at. Custom sorting is not yet supported.
  • Summary auto-scopes to 12 months. When no start_date/end_date is provided, /demo-sessions/summary defaults to the last 12 months.

Support

If you need assistance with the Customer Data API, your Walnut account team is here to help.

  • API key requests: Contact your Walnut account representative to provision a new key or revoke an existing one.
  • Technical issues: Include the request URL, timestamp, and HTTP status code when reporting problems.
  • Feature requests: Let your account team know what additional data or capabilities would be valuable for your use case.
Was this article helpful?
0 out of 0 found this helpful