Introduction

Pulse Analytics is a privacy-friendly, self-hosted web analytics platform built in Rust. It provides real-time pageview tracking, custom events, user segmentation, funnel analysis, A/B testing, surveys, error tracking, and more — all through a single, fast API.

Base URL

https://pulse.ayushojha.com

All API endpoints are relative to this base URL. The API accepts and returns JSON unless otherwise noted (e.g., CSV exports).

Production Runtime

The reference deployment uses the current stable Rust and Node toolchains, PostgreSQL 18, Redis 8, and Debian Trixie runtime images. Configure these environment variables before running the server:

VariablePurpose
DATABASE_URLPostgreSQL connection string used by migrations, ingestion, and query APIs.
REDIS_URLRedis connection string for buffering, rate limits, and background work.
PULSE_ADMIN_TOKENBearer token for project, key, module, and webhook administration.
PULSE_COOKIE_SECRETSecret used to sign dashboard cookies. Set explicitly in production.
EMAIL_REPORT_WEBHOOK_URLOptional delivery endpoint for scheduled email report payloads.
ALLOWED_ORIGINSComma-separated list of browser origins allowed to call the API.

Quick Start

Get Your API Keys

Register your project with the admin API and create ingest + query scoped API keys.

Add the Tracking Script

Drop a single <script> tag into your site’s HTML before the closing </body> tag.

View Your Dashboard

Open pulse.ayushojha.com/dashboard and log in with your query-scoped API key.

Quick Install
<!-- Add before </body> -->
<script defer
  src="https://pulse.ayushojha.com/api/script.js"
  data-api="https://pulse.ayushojha.com"
  data-key="pa_live_YOUR_INGEST_KEY"></script>

Authentication

Pulse uses API key authentication for all data operations and bearer-token authentication for admin endpoints.

API Key Authentication

Pass your project API key in one of two ways:

MethodExample
Header (preferred)X-Pulse-Key: pa_live_YOUR_KEY
Query parameter?key=pa_live_YOUR_KEY

Admin Token Authentication

Administrative endpoints (creating projects, managing keys) require the admin bearer token:

Admin Auth Header
Authorization: Bearer <PULSE_ADMIN_TOKEN>

API Key Scopes

Each API key has one or more scopes that determine its permissions:

ScopePermissionsTypical Use
ingestWrite tracking data (pageviews, events, errors, etc.)Frontend tracking script
queryRead analytics data (stats, reports, exports)Dashboard access, API queries
adminFull access including key managementBackend administration

Module Restrictions

API keys can optionally be restricted to specific modules via the allowed_modules field. When set, the key can only access the listed modules. When null or empty, the key has access to all enabled modules for the project.

Security tip: Create separate ingest and query keys for each project. Never expose query or admin keys on the frontend.

Module System

Pulse features a configurable module system that lets you enable or disable feature sets per project. Each module controls access to related API endpoints and data collection. Modules have access levels: read, write, or all (both).

Available Modules

Pulse ships with 33 modules across five categories:

Core

Core
core

Core analytics: pageviews, sessions, events, devices, geo, referrers, realtime

Core
identity

User profiles, IDs, aliases, traits, accounts, and SCIM provisioning

Analysis

Analysis
segments

Saved behavioral and identity-based visitor segments

Analysis
dashboards

Custom dashboards, saved reports, query explorer, and product insights

Analysis
funnels

Multi-step conversion funnels

Analysis
goals

Goal and conversion tracking

Analysis
retention

User retention cohort analysis

Analysis
cohorts

Grouped cohort metric comparison

Analysis
paths

User navigation path analysis

Analysis
bi

Safe SQL, embeds, external connections, semantic metrics, visual queries, and CSV uploads

Tracking

Tracking
utm

UTM campaign parameter tracking

Tracking
webvitals

Core Web Vitals (LCP, FCP, CLS, INP, TTFB)

Tracking
scroll

Scroll depth measurement per page

Tracking
revenue

Revenue and ecommerce event tracking

Tracking
search

Internal site search query tracking

Tracking
outlinks

External link clicks and file download tracking

Tracking
error_tracking

JavaScript error capture and grouping

Tracking
logs

Application log ingestion with release filtering

Experience & Experimentation

Experimentation
feature_flags

Feature flags, remote config, targeting rules, and rollout evaluation

Experimentation
ab_testing

A/B experiment management and results

Experience
session_replay

Privacy-masked session recording and playback

Experience
heatmaps

Click heatmap data collection

Experimentation
surveys

In-app survey builder and response collection

Advanced
ai_queries

Natural-language analytics answers, generated insights, and query history

Advanced
predictions

Predictive analytics and churn probability

Platform

Platform
exports

CSV data export for all report types

Platform
integrations

Integrations marketplace catalog for sources, destinations, SDKs, and imports

Platform
sources

Source catalog, webhook collection, ingestion audit, and source tokens

Platform
destinations

Destination catalog, event routing, retries, and dead letters

Platform
sharing

Password-protected shared dashboards

Platform
governance

Tracking plans, event schemas, data dictionary, and quality monitoring

Platform
email_reports

Scheduled analytics digests via delivery webhook

Platform
alerts

Threshold-based alert rules and notifications

Module Admin Endpoints

GET /api/admin/projects/{id}/modules

List all modules and their current status for a project.

Request
curl -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules
Response 200
{
  "modules": [
    { "name": "pageviews", "enabled": true, "access": "all" },
    { "name": "events", "enabled": true, "access": "all" },
    { "name": "funnels", "enabled": false, "access": "read" },
    { "name": "ab_testing", "enabled": false, "access": "read" }
  ]
}
PUT /api/admin/projects/{id}/modules

Bulk update module settings for a project.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "modules": [
      {"name": "funnels", "enabled": true, "access": "all"},
      {"name": "ab_testing", "enabled": true, "access": "all"},
      {"name": "heatmaps", "enabled": false}
    ]
  }' \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules
POST /api/admin/projects/{id}/modules/{name}/enable

Enable a single module.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/funnels/enable
POST /api/admin/projects/{id}/modules/{name}/disable

Disable a single module. Existing data is preserved but API access is revoked.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/heatmaps/disable
PUT /api/admin/projects/{id}/modules/{name}/access

Set the access level for a module: read, write, or all.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"access": "read"}' \
  https://pulse.ayushojha.com/api/admin/projects/4c3654c3-9274-4fea-bc3e-30b8edcd7f7a/modules/surveys/access

Tracking Script

The easiest way to integrate Pulse is with the lightweight JavaScript tracking script. It automatically captures pageviews, handles SPAs, and supports optional modules like Web Vitals, scroll depth, outbound link tracking, and more.

HTML Snippet Installation

HTML
<script defer
  src="https://pulse.ayushojha.com/api/script.js"
  data-api="https://pulse.ayushojha.com"
  data-key="pa_live_YOUR_INGEST_KEY"
  data-utm="true"
  data-vitals="true"
  data-scroll="true"
  data-outlinks="true"
  data-errors="true"
  data-clicks="true"
  data-replay="true"
  data-replay-sample="0.25"
  data-replay-mask="true"
  data-search="true"
  data-search-param="q"
  data-consent-mode="analytics"
  data-consent-granted="true"
  data-release="2026.05.09"
  data-environment="production"
  data-dnt="true"></script>

Data Attributes

AttributeDefaultDescription
data-keyRequired. Your project ingest API key.
data-apihttps://pulse.ayushojha.comAPI base URL. Override for self-hosted instances.
data-utmfalseCapture UTM parameters from query strings.
data-vitalsfalseCollect Core Web Vitals (LCP, FCP, CLS, INP, TTFB).
data-scrollfalseTrack maximum scroll depth per page.
data-outlinksfalseTrack clicks on outbound links.
data-errorsfalseCapture unhandled JavaScript errors.
data-clicksfalseRecord click coordinates for heatmaps.
data-replayfalseRecord privacy-masked session replay events.
data-replay-sample1Replay sampling rate from 0 to 1.
data-replay-masktrueMask form input values in replay events.
data-searchfalseTrack internal site search queries.
data-search-paramqURL query parameter name for search queries.
data-consent-modeanalyticsConsent mode sent with collection requests.
data-consent-grantedtrueSet to false until a visitor grants consent when consent is required.
data-releaseRelease/version tag attached to captured errors and logs.
data-environmentproductionEnvironment tag attached to captured errors and logs.
data-dnttrueRespect browser Do Not Track setting.

JavaScript API

Use the global pulse() function to send custom data programmatically:

Custom Event Tracking
JavaScript
// Track a custom event
pulse("event", {
  name: "button_click",
  data: { button_id: "cta-hero", variant: "blue" }
});

// Track an event with revenue
pulse("event", {
  name: "purchase",
  revenue_amount: 49.99,
  revenue_currency: "USD",
  data: { plan: "pro", billing: "annual" }
});
Search Query Tracking
JavaScript
// Track a search query
pulse("search", {
  query: "deployment guide",
  results_count: 12
});
Application Log Submission
JavaScript
// Submit an application log with release metadata from the script config
pulse("log", "warn", "Checkout retry", {
  order_id: "ord_123",
  retry_count: 2
});
Survey Response Submission
JavaScript
// Submit survey response
pulse("survey_response", {
  survey_id: "c7f3e8a2-91b4-4d6e-b2f1-8a3c9d4e5f6a",
  answers: [
    { question_id: "q1", value: "Very satisfied" },
    { question_id: "q2", value: 9 }
  ],
  completed: true
});

TypeScript SDK

Client-side (Browser)
Install
npm install @ayushojha/pulse-analytics
TypeScript
import { PulseClient } from '@ayushojha/pulse-analytics';

const pulse = new PulseClient({
  apiKey: 'pa_live_YOUR_INGEST_KEY',
  apiUrl: 'https://pulse.ayushojha.com',
  trackUtm: true,
  trackWebVitals: true,
  trackScrollDepth: true
});

// Track custom events
pulse.event('signup_complete', { method: 'google' });

// Track revenue
pulse.event('purchase', { plan: 'pro' }, 29.99, 'USD');
Server-side (Node.js)
TypeScript
import { PulseServerClient } from '@ayushojha/pulse-analytics/server';

const pulse = new PulseServerClient({
  apiKey: 'pa_live_YOUR_INGEST_KEY',
  apiUrl: 'https://pulse.ayushojha.com'
});

// Server-side event
await pulse.trackEvent({
  visitorId: 'visitor_123',
  eventName: 'subscription_renewed',
  data: { plan: 'enterprise', mrr: 499 }
});
React / Vue / Next.js helpers
React
import { PulseProvider, usePulsePageview } from '@ayushojha/pulse-analytics/react';

function App({ children }) {
  return (
    <PulseProvider config={{ apiKey: 'pa_live_YOUR_INGEST_KEY', apiUrl: 'https://pulse.ayushojha.com' }}>
      {children}
    </PulseProvider>
  );
}

function Page() {
  usePulsePageview();
}
Vue
import { createApp } from 'vue';
import { createPulseVue, usePulsePageview } from '@ayushojha/pulse-analytics/vue';

const app = createApp(App);
app.use(createPulseVue({
  config: { apiKey: 'pa_live_YOUR_INGEST_KEY', apiUrl: 'https://pulse.ayushojha.com' }
}));

export default {
  setup() {
    usePulsePageview();
  }
};
Next.js Script
import Script from 'next/script';
import { getPulseScriptProps } from '@ayushojha/pulse-analytics/next';

<Script
  {...getPulseScriptProps({
    apiKey: 'pa_live_YOUR_INGEST_KEY',
    apiUrl: 'https://pulse.ayushojha.com',
    trackWebVitals: true,
    strategy: 'afterInteractive'
  })}
/>
React Native mobile SDK
React Native
import { createPulseNative } from '@ayushojha/pulse-analytics/react-native';

const pulse = createPulseNative({
  apiKey: 'pa_live_YOUR_INGEST_KEY',
  apiUrl: 'https://pulse.ayushojha.com',
  visitorId: 'device_or_user_scoped_id',
  environment: 'production'
});

await pulse.screen('Home');
await pulse.event('purchase', { plan: 'pro' }, 29.99, 'USD');
await pulse.identify('user_123', { plan: 'pro' });

Ingestion API

All tracking data flows through a single universal collection endpoint. The payload type determines which module processes the data.

POST /api/collect

Universal data collection endpoint. Accepts any payload type.

HeaderValue
X-Pulse-KeyYour ingest-scoped API key
Content-Typeapplication/json

Payload Types

POST  pageview

Record a page view. Sent automatically by the tracking script.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "pageview",
    "path": "/pricing",
    "title": "Pricing - Acme Corp",
    "referrer": "https://google.com",
    "screen": "1920x1080",
    "language": "en-US",
    "utm_source": "google",
    "utm_medium": "cpc",
    "utm_campaign": "spring_sale",
    "utm_content": "banner_a",
    "utm_term": "analytics tool"
  }'
Response 200
{ "ok": true }
POST  event

Track a custom event with optional metadata and revenue.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "event",
    "name": "add_to_cart",
    "path": "/products/widget-pro",
    "data": {
      "product_id": "prod_8x92k",
      "product_name": "Widget Pro",
      "quantity": 2
    },
    "revenue_amount": 59.98,
    "revenue_currency": "USD"
  }'
POST  identify

Persist a user profile for the current visitor. Include user_id when the visitor is known; anonymous trait-only identifies are also stored.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "identify",
    "visitor_id": "v_7fz9k2",
    "payload": {
      "user_id": "user_123",
      "traits": {
        "plan": "pro",
        "company": "Acme Corp",
        "role": "admin",
        "signed_up": "2025-08-14"
      }
    }
  }'
POST  web_vital

Submit a Core Web Vital measurement.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "web_vital",
    "name": "LCP",
    "value": 1842.5,
    "rating": "good",
    "path": "/home"
  }'
POST  scroll_depth

Report the maximum scroll depth reached on a page (0-100).

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "scroll_depth",
    "path": "/blog/getting-started",
    "max_depth": 87
  }'
POST  search_query

Track an internal site search query and result count.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "search_query",
    "query": "pricing plans",
    "results_count": 7,
    "path": "/search"
  }'
POST  outlink

Track an outbound link click.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "outlink",
    "url": "https://github.com/acme/widget",
    "link_type": "external",
    "path": "/docs"
  }'
POST  js_error

Report a JavaScript error with stack trace.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "js_error",
    "visitor_id": "v_7fz9k2",
    "payload": {
      "message": "TypeError: Cannot read properties of null (reading '\''id'\'')",
      "stack": "TypeError: Cannot read properties of null\n    at handleClick (app.js:142:18)\n    at onClick (react-dom.js:3905:14)",
      "filename": "https://acme.com/assets/app.js",
      "lineno": 142,
      "colno": 18,
      "path": "/dashboard",
      "release": "2026.05.09",
      "environment": "production"
    }
  }'
POST  log

Submit an application log entry for release-aware troubleshooting.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "log",
    "visitor_id": "v_7fz9k2",
    "payload": {
      "level": "warn",
      "message": "Checkout retry",
      "body": { "order_id": "ord_123", "retry_count": 2 },
      "path": "/checkout",
      "release": "2026.05.09",
      "environment": "production"
    }
  }'
POST  click_event

Record a click event with coordinates for heatmap generation.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "click_event",
    "path": "/pricing",
    "x": 482,
    "y": 1203,
    "element_selector": "button.cta-primary",
    "viewport_width": 1440,
    "viewport_height": 900
  }'
POST  survey_response

Submit a user’s response to an active survey.

Request
curl -X POST https://pulse.ayushojha.com/api/collect \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "survey_response",
    "visitor_id": "v_7fz9k2",
    "payload": {
      "survey_id": "c7f3e8a2-91b4-4d6e-b2f1-8a3c9d4e5f6a",
      "answers": [
        {"question_id": "q1", "value": "Very satisfied"},
        {"question_id": "q2", "value": 9},
        {"question_id": "q3", "value": "Love the real-time dashboard!"}
      ],
      "completed": true,
      "path": "/app/settings"
    }
  }'

Core Analytics API

These endpoints are always available regardless of module configuration. They provide the fundamental analytics data for your project. All endpoints require a query-scoped API key.

Common query parameters: All core endpoints accept start_at (ISO 8601), end_at (ISO 8601), limit (default 10), and offset (default 0) unless otherwise noted.
GET /api/v1/stats

Overview metrics for the specified date range, with comparison to the previous period of equal length.

ParameterTypeDescription
start_atstringPeriod start (ISO 8601). Default: 30 days ago.
end_atstringPeriod end (ISO 8601). Default: now.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/stats?start_at=2026-03-01T00:00:00Z&end_at=2026-03-22T23:59:59Z"
Response 200
{
  "current": {
    "pageviews": 14832,
    "visitors": 3241,
    "sessions": 4567,
    "bounce_rate": 42.3,
    "avg_duration": 187.5,
    "events": 2891
  },
  "previous": {
    "pageviews": 12104,
    "visitors": 2890,
    "sessions": 3921,
    "bounce_rate": 45.1,
    "avg_duration": 162.8,
    "events": 2340
  }
}
GET /api/v1/stats/timeseries

Daily time series of core metrics within the date range.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/stats/timeseries?start_at=2026-03-15&end_at=2026-03-22"
Response 200
{
  "data": [
    { "date": "2026-03-15", "pageviews": 687, "visitors": 198, "sessions": 245 },
    { "date": "2026-03-16", "pageviews": 524, "visitors": 161, "sessions": 203 },
    { "date": "2026-03-17", "pageviews": 731, "visitors": 212, "sessions": 278 },
    { "date": "2026-03-18", "pageviews": 812, "visitors": 234, "sessions": 301 },
    { "date": "2026-03-19", "pageviews": 695, "visitors": 189, "sessions": 256 },
    { "date": "2026-03-20", "pageviews": 903, "visitors": 267, "sessions": 342 },
    { "date": "2026-03-21", "pageviews": 778, "visitors": 221, "sessions": 289 },
    { "date": "2026-03-22", "pageviews": 411, "visitors": 134, "sessions": 167 }
  ]
}
GET /api/v1/pages

Top pages ranked by views.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/pages?limit=5"
Response 200
{
  "data": [
    { "path": "/", "views": 4821, "unique_views": 2190, "avg_duration": 45.2 },
    { "path": "/pricing", "views": 2314, "unique_views": 1820, "avg_duration": 92.7 },
    { "path": "/docs/getting-started", "views": 1892, "unique_views": 1456, "avg_duration": 214.3 },
    { "path": "/blog/analytics-guide", "views": 1203, "unique_views": 987, "avg_duration": 312.1 },
    { "path": "/signup", "views": 891, "unique_views": 834, "avg_duration": 67.8 }
  ],
  "total": 47
}
GET /api/v1/referrers

Traffic sources ranked by visitor count.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/referrers?limit=5"
Response 200
{
  "data": [
    { "source": "(direct)", "visitors": 1245, "pageviews": 3891 },
    { "source": "google.com", "visitors": 892, "pageviews": 2104 },
    { "source": "twitter.com", "visitors": 341, "pageviews": 567 },
    { "source": "github.com", "visitors": 287, "pageviews": 445 },
    { "source": "reddit.com", "visitors": 198, "pageviews": 312 }
  ]
}
GET /api/v1/events

Custom events ranked by count.

Response 200
{
  "data": [
    { "name": "signup_complete", "count": 423, "unique": 412 },
    { "name": "add_to_cart", "count": 312, "unique": 287 },
    { "name": "purchase", "count": 89, "unique": 84 },
    { "name": "newsletter_subscribe", "count": 67, "unique": 65 }
  ]
}
GET /api/v1/devices

Visitor breakdown by browser, operating system, and device type.

Response 200
{
  "browsers": [
    { "name": "Chrome", "visitors": 1842, "percentage": 56.8 },
    { "name": "Safari", "visitors": 723, "percentage": 22.3 },
    { "name": "Firefox", "visitors": 412, "percentage": 12.7 }
  ],
  "operating_systems": [
    { "name": "Windows", "visitors": 1456, "percentage": 44.9 },
    { "name": "macOS", "visitors": 987, "percentage": 30.5 },
    { "name": "iOS", "visitors": 534, "percentage": 16.5 }
  ],
  "device_types": [
    { "name": "Desktop", "visitors": 2198, "percentage": 67.8 },
    { "name": "Mobile", "visitors": 845, "percentage": 26.1 },
    { "name": "Tablet", "visitors": 198, "percentage": 6.1 }
  ]
}
GET /api/v1/geo

Visitor distribution by country.

Response 200
{
  "data": [
    { "country": "US", "visitors": 1567, "percentage": 48.3 },
    { "country": "GB", "visitors": 412, "percentage": 12.7 },
    { "country": "DE", "visitors": 298, "percentage": 9.2 },
    { "country": "IN", "visitors": 234, "percentage": 7.2 },
    { "country": "CA", "visitors": 189, "percentage": 5.8 }
  ]
}
GET /api/v1/realtime

Count of active visitors on the site right now (within the last 5 minutes).

Response 200
{
  "active_visitors": 23
}

Identity

Requires the identity module. Identify calls create or update visitor profiles, merge traits, record aliases when a stable user_id is provided, and can attach visitors to account or company records with account-level analytics. Local SCIM provisioning APIs can create users and groups without depending on a live external identity provider.

GET /api/v1/identity/users

List stored user profiles. Supports limit and offset.

Response 200
{
  "data": [
    {
      "visitor_id": "v_7fz9k2",
      "user_id": "user_123",
      "traits": { "plan": "pro" },
      "last_seen_at": "2026-05-09T18:22:14Z"
    }
  ]
}
GET /api/v1/identity/users/{visitor_id}

Fetch one visitor profile and its merged traits.

GET /api/v1/identity/aliases/{user_id}

List visitor IDs aliased to a known user ID.

GET /api/v1/identity/graph

Return visitor, user, and account nodes plus alias, identify, and membership edges. Provide one or more of visitor_id, user_id, or account_id; optional limit caps graph expansion.

GET /api/v1/identity/accounts

List account or company profiles captured from identify calls. Supports limit and offset.

GET /api/v1/identity/accounts/{account_id}

Fetch one account profile with merged account traits.

GET /api/v1/identity/accounts/{account_id}/members

List visitors and user IDs associated with an account, including role and last-seen timestamps.

GET /api/v1/identity/accounts/{account_id}/analytics

Return account-level members, identified users, sessions, pageviews, events, and revenue for a date range.

GET /api/v1/scim/users

List locally provisioned SCIM users. Supports limit and offset. Provisioned users are also synced into identity profiles with visitor IDs like scim:{id}.

POST /api/v1/scim/users

Create a provisioned user with normalized profile fields, email array, active state, and arbitrary traits.

Request
{
  "user_name": "alice@example.com",
  "external_id": "idp_123",
  "active": true,
  "display_name": "Alice Example",
  "emails": [{ "value": "alice@example.com", "primary": true }],
  "traits": { "department": "Engineering" }
}
GET PUT DELETE /api/v1/scim/users/{id}

Fetch, update, or delete one provisioned user. Updates refresh the synced identity profile.

GET /api/v1/scim/groups

List locally provisioned groups. Supports limit and offset.

POST /api/v1/scim/groups

Create a provisioned group and replace its member list with existing SCIM user IDs.

Request
{
  "display_name": "Engineering",
  "external_id": "group_eng",
  "traits": { "cost_center": "eng" },
  "members": ["6d0aa190-5b2a-4b0e-9c8d-df7608e7aa8f"]
}
GET PUT DELETE /api/v1/scim/groups/{id}

Fetch one provisioned group with expanded members, replace its group details and membership, or delete it.

UTM / Campaign Tracking

Requires the utm module to be enabled. Provides campaign-level analytics from UTM parameters captured during pageviews.

GET /api/v1/campaigns

Campaign performance metrics grouped by UTM campaign name.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/campaigns?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "data": [
    {
      "campaign": "spring_sale",
      "visitors": 892,
      "pageviews": 2341,
      "bounce_rate": 38.2,
      "avg_duration": 142.5
    },
    {
      "campaign": "product_launch",
      "visitors": 456,
      "pageviews": 1023,
      "bounce_rate": 44.7,
      "avg_duration": 98.3
    }
  ]
}
GET /api/v1/campaigns/sources

Top UTM sources (e.g., google, newsletter, twitter).

Response 200
{
  "data": [
    { "source": "google", "visitors": 1234, "pageviews": 3456 },
    { "source": "newsletter", "visitors": 567, "pageviews": 892 },
    { "source": "twitter", "visitors": 345, "pageviews": 512 }
  ]
}
GET /api/v1/campaigns/mediums

Top UTM mediums (e.g., cpc, email, social).

Response 200
{
  "data": [
    { "medium": "cpc", "visitors": 1102, "pageviews": 2891 },
    { "medium": "email", "visitors": 678, "pageviews": 1234 },
    { "medium": "social", "visitors": 445, "pageviews": 678 }
  ]
}
GET /api/v1/campaigns/timeseries

Daily time series for a specific UTM source.

ParameterTypeDescription
utm_sourcestringRequired. Filter by UTM source.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/campaigns/timeseries?utm_source=google&start_at=2026-03-18&end_at=2026-03-22"
Response 200
{
  "data": [
    { "date": "2026-03-18", "visitors": 89, "pageviews": 234 },
    { "date": "2026-03-19", "visitors": 102, "pageviews": 287 },
    { "date": "2026-03-20", "visitors": 78, "pageviews": 198 },
    { "date": "2026-03-21", "visitors": 95, "pageviews": 256 },
    { "date": "2026-03-22", "visitors": 67, "pageviews": 178 }
  ]
}

Marketing Analytics

Requires the utm module. Marketing analytics adds channel grouping, attribution models, ecommerce revenue reports, AI referrer detection, and vendor-export imports for Google Analytics, Google Ads, and Search Console style data.

GET /api/v1/marketing/channels

Group traffic into Direct, Organic Search, Paid Search, Organic Social, Paid Social, Email, Display, Affiliate, AI Referrals, Referral, Paid Other, and Other. Supports start_at and end_at.

GET /api/v1/marketing/attribution

Attribute revenue events by channel, source, and campaign. Supports model values first_touch, last_touch, and linear.

GET /api/v1/marketing/ecommerce

Return total orders, revenue, average order value, currency breakdown, and top revenue products from events with revenue_amount.

GET /api/v1/marketing/ai-referrers

Detect traffic from ChatGPT/OpenAI, Perplexity, Claude/Anthropic, Gemini, and Copilot referrers.

GET /api/v1/marketing/imports

List imported marketing export batches. Optional provider filters support google_analytics, google_ads, and search_console; pagination uses limit and offset.

POST /api/v1/marketing/imports

Import rows from GA4, Google Ads, or Search Console exports without requiring live Google OAuth. Each row stores a date, dimension object, numeric metrics, and the original raw row for auditability.

Request
{
  "provider": "google_ads",
  "name": "May brand campaigns",
  "rows": [
    {
      "date": "2026-05-01",
      "dimensions": { "campaign": "brand", "source": "google" },
      "metrics": { "impressions": 12000, "clicks": 420, "cost": 318.44, "conversions": 31 },
      "raw_row": { "Campaign": "brand", "Clicks": "420" }
    }
  ],
  "metadata": { "source": "csv_export" }
}
GET /api/v1/marketing/imports/{id}/rows

Page through imported rows for one batch. Use DELETE /api/v1/marketing/imports/{id} to remove a batch and all rows.

GET /api/v1/marketing/imports/summary

Summarize imported rows by date range and optional provider, returning rows, impressions, clicks, cost, conversions, revenue, sessions, and users.

Segments

Requires the segments module. Save reusable visitor definitions from identity traits, session properties, pageviews, events, and behavioral metrics, then evaluate, compare, and break them down by key properties.

Segment Definition

A segment definition has a match mode of all or any, plus a list of conditions. Supported sources are profile, session, pageview, event, and metric. Supported operators are exists, not_exists, eq, neq, contains, starts_with, ends_with, gt, gte, lt, lte, and in.

Definition Example
{
  "match": "all",
  "conditions": [
    {
      "source": "profile",
      "field": "traits.plan",
      "op": "eq",
      "value": "pro"
    },
    {
      "source": "event",
      "event": "purchase",
      "field": "event_data.amount",
      "op": "gte",
      "value": 100
    }
  ]
}
GET /api/v1/segments

List saved segments for the project.

Response 200
{
  "data": [
    {
      "id": "9f3d2e74-0a2b-45b7-a1d4-3b9fc6e4f2c8",
      "name": "High-value pro users",
      "description": "Pro users with recent purchases over $100",
      "definition": { "match": "all", "conditions": [] },
      "is_active": true
    }
  ]
}
POST /api/v1/segments

Create a saved segment.

Request
curl -X POST -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{"name":"High-value pro users","description":"Pro users with recent purchases","definition":{"match":"all","conditions":[{"source":"profile","field":"traits.plan","op":"eq","value":"pro"},{"source":"event","event":"purchase","field":"event_data.amount","op":"gte","value":100}]}}' \
  https://pulse.ayushojha.com/api/v1/segments
GET /api/v1/segments/{id}

Fetch one saved segment.

PUT /api/v1/segments/{id}

Update a segment’s name, description, definition, or active state.

DELETE /api/v1/segments/{id}

Delete a saved segment.

GET /api/v1/segments/{id}/evaluate

Evaluate a segment over a date range and return matching visitor IDs. Supports start_at, end_at, limit, and offset.

Response 200
{
  "segment_id": "9f3d2e74-0a2b-45b7-a1d4-3b9fc6e4f2c8",
  "total_visitors": 128,
  "visitors": ["v_7fz9k2", "v_h3ab11"]
}
GET /api/v1/segments/compare

Compare traffic and conversion metrics across multiple saved segments. Pass comma-separated IDs via segment_ids.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/segments/compare?segment_ids=9f3d2e74-0a2b-45b7-a1d4-3b9fc6e4f2c8,37b29685-1f73-4b72-88db-f6c71da79081&start_at=2026-04-01T00:00:00Z&end_at=2026-05-01T00:00:00Z"
GET /api/v1/segments/{id}/breakdown

Break down a segment by country, device, browser, os, path, event, or identity traits such as trait:plan. Supports start_at, end_at, and limit.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/segments/9f3d2e74-0a2b-45b7-a1d4-3b9fc6e4f2c8/breakdown?property=trait:plan"
Response 200
{
  "data": [
    { "value": "pro", "visitors": 93 },
    { "value": "business", "visitors": 35 }
  ]
}

Funnels

Requires the funnels module. Define multi-step conversion funnels to understand where users drop off in key flows.

GET /api/v1/funnels

List all funnels for the project.

Response 200
{
  "data": [
    {
      "id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
      "name": "Signup Flow",
      "steps": 4,
      "created_at": "2026-02-14T10:30:00Z"
    }
  ]
}
POST /api/v1/funnels

Create a new funnel. Steps are evaluated in order.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signup Flow",
    "steps": [
      {"type": "pageview", "value": "/", "label": "Homepage"},
      {"type": "pageview", "value": "/pricing", "label": "Pricing Page"},
      {"type": "pageview", "value": "/signup", "label": "Signup Page"},
      {"type": "event", "value": "signup_complete", "label": "Signup Complete"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/funnels
Response 201
{
  "id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
  "name": "Signup Flow",
  "steps": [
    { "type": "pageview", "value": "/", "label": "Homepage" },
    { "type": "pageview", "value": "/pricing", "label": "Pricing Page" },
    { "type": "pageview", "value": "/signup", "label": "Signup Page" },
    { "type": "event", "value": "signup_complete", "label": "Signup Complete" }
  ],
  "created_at": "2026-03-22T14:30:00Z"
}
GET /api/v1/funnels/{id}

Get a specific funnel definition.

PUT /api/v1/funnels/{id}

Update a funnel’s name or steps.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signup Flow v2",
    "steps": [
      {"type": "pageview", "value": "/", "label": "Homepage"},
      {"type": "pageview", "value": "/signup", "label": "Signup Page"},
      {"type": "event", "value": "signup_complete", "label": "Signup Complete"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/funnels/f1a2b3c4-5678-9abc-def0-1234567890ab
DELETE /api/v1/funnels/{id}

Delete a funnel. Returns 204 No Content on success.

GET /api/v1/funnels/{id}/analyze

Analyze funnel performance. Returns visitor counts and drop-off rates for each step.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/funnels/f1a2b3c4-5678-9abc-def0-1234567890ab/analyze?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "funnel_id": "f1a2b3c4-5678-9abc-def0-1234567890ab",
  "name": "Signup Flow",
  "steps": [
    { "label": "Homepage", "visitors": 3241, "drop_off": 0, "conversion_rate": 100.0 },
    { "label": "Pricing Page", "visitors": 1820, "drop_off": 1421, "conversion_rate": 56.2 },
    { "label": "Signup Page", "visitors": 834, "drop_off": 986, "conversion_rate": 45.8 },
    { "label": "Signup Complete", "visitors": 412, "drop_off": 422, "conversion_rate": 49.4 }
  ],
  "overall_conversion": 12.7
}

Goals / Conversions

Requires the goals module. Define conversion goals based on pageviews, events, session duration, or pages per session, and track their performance.

GET /api/v1/goals

List all configured goals.

Response 200
{
  "data": [
    {
      "id": "g9a8b7c6-5432-1fed-cba0-987654321012",
      "name": "Completed Purchase",
      "goal_type": "event",
      "config": { "event_name": "purchase" },
      "created_at": "2026-01-20T08:00:00Z"
    },
    {
      "id": "g1b2c3d4-5678-9abc-def0-abcdef123456",
      "name": "Visited Pricing",
      "goal_type": "pageview",
      "config": { "path": "/pricing" },
      "created_at": "2026-01-20T08:15:00Z"
    }
  ]
}
POST /api/v1/goals

Create a new goal. Supported types: pageview, event, duration, pages_per_session.

Request — Event Goal
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Completed Purchase",
    "goal_type": "event",
    "config": {
      "event_name": "purchase"
    }
  }' \
  https://pulse.ayushojha.com/api/v1/goals
Request — Duration Goal
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engaged Session (>2min)",
    "goal_type": "duration",
    "config": {
      "threshold_seconds": 120
    }
  }' \
  https://pulse.ayushojha.com/api/v1/goals
GET /api/v1/goals/{id}

Get a specific goal definition.

PUT /api/v1/goals/{id}

Update a goal’s name or configuration.

DELETE /api/v1/goals/{id}

Delete a goal. Returns 204 No Content.

GET /api/v1/goals/{id}/stats

Get conversion statistics for a specific goal.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/goals/g9a8b7c6-5432-1fed-cba0-987654321012/stats?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012",
  "name": "Completed Purchase",
  "conversions": 89,
  "unique_visitors": 84,
  "total_revenue": 4287.56,
  "conversion_rate": 2.59
}

Retention

Requires the retention module. Analyze how well your product retains users over time with cohort-based retention tables.

GET /api/v1/retention

Get retention cohorts showing return rates at various intervals.

ParameterTypeDescription
periodstringCohort period: daily, weekly (default), or monthly.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/retention?period=weekly&start_at=2026-02-01&end_at=2026-03-22"
Response 200
{
  "period": "weekly",
  "cohorts": [
    {
      "cohort": "2026-02-03",
      "initial_visitors": 412,
      "D1": 68.2,
      "D7": 42.5,
      "D14": 31.8,
      "D30": 22.1,
      "D60": 14.3,
      "D90": null
    },
    {
      "cohort": "2026-02-10",
      "initial_visitors": 387,
      "D1": 71.0,
      "D7": 45.2,
      "D14": 33.4,
      "D30": 24.8,
      "D60": null,
      "D90": null
    },
    {
      "cohort": "2026-02-17",
      "initial_visitors": 445,
      "D1": 65.8,
      "D7": 39.7,
      "D14": 28.9,
      "D30": 19.6,
      "D60": null,
      "D90": null
    }
  ]
}

Cohorts

Requires the cohorts module. Group visitors into cohorts and compare metrics across time periods.

GET /api/v1/cohorts

Get cohort analysis data grouped by week or month.

ParameterTypeDescription
group_bystringGrouping period: week (default) or month.
metricstringMetric to compare: pageviews, sessions, events, or revenue.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/cohorts?group_by=month&metric=sessions&start_at=2026-01-01&end_at=2026-03-22"
Response 200
{
  "group_by": "month",
  "metric": "sessions",
  "cohorts": [
    { "period": "2026-01", "visitors": 2890, "total": 3921, "avg_per_visitor": 1.36 },
    { "period": "2026-02", "visitors": 3105, "total": 4234, "avg_per_visitor": 1.36 },
    { "period": "2026-03", "visitors": 3241, "total": 4567, "avg_per_visitor": 1.41 }
  ]
}

Path Analysis

Requires the paths module. Discover the most common navigation flows before or after a given page.

GET /api/v1/paths

Get page flow data showing where visitors go to or come from.

ParameterTypeDescription
pathstringRequired. The pivot page path (e.g., /pricing).
directionstringforward (default) — where visitors go next. backward — where they came from.
limitintegerNumber of paths to return. Default: 10.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/paths?path=/pricing&direction=forward&limit=5"
Response 200
{
  "path": "/pricing",
  "direction": "forward",
  "flows": [
    { "next_path": "/signup", "visitors": 456, "percentage": 25.1 },
    { "next_path": "/", "visitors": 312, "percentage": 17.1 },
    { "next_path": "/features", "visitors": 234, "percentage": 12.9 },
    { "next_path": "/docs", "visitors": 178, "percentage": 9.8 },
    { "next_path": "(exit)", "visitors": 640, "percentage": 35.2 }
  ]
}

Dashboards / Reports

Requires the dashboards module. Build custom dashboard layouts, persist report definitions, and run a safe query explorer over built-in analytics datasets. Supported report types are stats, timeseries, pages, referrers, events, devices, geo, and campaigns.

GET /api/v1/dashboards

List custom dashboards for the project.

POST /api/v1/dashboards

Create a dashboard layout with a validated widget array.

Request
{
  "name": "Growth Overview",
  "description": "Acquisition and conversion health",
  "layout": { "columns": 12 },
  "widgets": [
    { "id": "trend", "report_type": "timeseries", "x": 0, "y": 0, "w": 8, "h": 4 },
    { "id": "sources", "report_type": "referrers", "x": 8, "y": 0, "w": 4, "h": 4 }
  ],
  "is_default": true
}
GET /api/v1/dashboards/{id}

Fetch one dashboard. Use PUT /api/v1/dashboards/{id} to update it, or DELETE /api/v1/dashboards/{id} to remove it.

GET /api/v1/reports

List saved report definitions.

POST /api/v1/reports

Save a reusable report definition for dashboards, sharing workflows, or API clients.

Request
{
  "name": "Top pages",
  "report_type": "pages",
  "params": {
    "start_at": "2026-05-01T00:00:00Z",
    "end_at": "2026-05-09T23:59:59Z",
    "limit": 25
  },
  "visualization": "table",
  "is_active": true
}
GET /api/v1/reports/{id}

Fetch one saved report. Use PUT /api/v1/reports/{id} to update it, or DELETE /api/v1/reports/{id} to remove it.

POST /api/v1/reports/{id}/run

Run a saved report. Optional start_at and end_at query parameters override the saved date range.

POST /api/v1/query-explorer

Run an ad hoc safe analytics query. The explorer does not accept raw SQL.

Request
{
  "report_type": "campaigns",
  "start_at": "2026-05-01T00:00:00Z",
  "end_at": "2026-05-09T23:59:59Z",
  "limit": 20,
  "offset": 0
}
Response 200
{
  "run": {
    "id": "41078344-1d28-4c3a-80f6-b460f8718d4f",
    "report_type": "campaigns",
    "row_count": 8,
    "result": { "data": [] }
  },
  "summary": "Returned 8 campaign rows."
}
GET /api/v1/query-explorer/history

List prior explorer runs. Supports limit and offset.

Product Insights

Requires the dashboards module. Product insight endpoints compute lifecycle, activation, stickiness, and before/after impact metrics from existing pageview, event, session, and error data.

GET /api/v1/product/stickiness

Return DAU, WAU, MAU, DAU/WAU, WAU/MAU, DAU/MAU, and daily active visitor periods. Supports start_at and end_at.

GET /api/v1/product/lifecycle

Break active visitors into new, returning, resurrected, and dormant groups by comparing the selected range with the immediately preceding range.

POST /api/v1/product/activation

Measure the share of active visitors who completed all required events and page paths in a date range.

Request
{
  "start_at": "2026-05-01T00:00:00Z",
  "end_at": "2026-05-09T23:59:59Z",
  "event_names": ["signup_completed", "workspace_created"],
  "paths": ["/onboarding/done"]
}
POST /api/v1/product/impact

Compare equal before/after windows around a launch, release, campaign, or experiment date. Supported metrics are pageviews, visitors, sessions, events, and errors.

Request
{
  "metric": "events",
  "event_name": "checkout_completed",
  "split_at": "2026-05-09T00:00:00Z",
  "window_days": 7
}

BI Layer

Requires the bi module. The BI layer provides governed semantic metrics, external Postgres connections, embedded analytics links, row-level policies, saved read-only SQL, query run history, a visual query builder over approved datasets, drill-through detail rows, and JSON-row CSV uploads.

GET /api/v1/bi/metrics

List semantic metric definitions. Use POST /api/v1/bi/metrics to create one with key, name, dataset, and expression.

GET /api/v1/bi/metrics/{id}

Fetch one metric. Use PUT /api/v1/bi/metrics/{id} to update it, or DELETE /api/v1/bi/metrics/{id} to remove it.

GET /api/v1/bi/connections

List external BI database connections. Responses include a masked connection string, allowed schemas, status, last test time, and last error. Use POST /api/v1/bi/connections to register a Postgres connection.

POST /api/v1/bi/connections

Create a Postgres connection for external warehouse queries. Use least-privileged read-only credentials; Pulse runs external SQL in a read-only transaction and applies the configured search path.

Request
{
  "name": "Warehouse",
  "database_type": "postgres",
  "connection_string": "postgresql://readonly:secret@warehouse.example.com/analytics",
  "allowed_schemas": ["public", "analytics"],
  "is_active": true
}
GET /api/v1/bi/connections/{id}

Fetch one external connection. Use PUT /api/v1/bi/connections/{id} to rotate credentials or update schemas, or DELETE /api/v1/bi/connections/{id} to remove it.

POST /api/v1/bi/connections/{id}/test

Open the external database, set a read-only transaction and search path, run SELECT 1, and persist last_tested_at plus any connection error.

POST /api/v1/bi/connections/{id}/query

Run read-only external SQL against a registered connection. SQL must start with SELECT or WITH, cannot include comments, separators, mutating keywords, or {{project_id}}; each run is stored as external_sql history.

Request
{
  "sql_text": "SELECT account_id, plan, mrr FROM accounts ORDER BY mrr DESC",
  "limit": 100
}
GET /api/v1/bi/embeds

List white-label BI embed definitions. Responses include resource type, origin allowlist, theme, token prefix, access count, and last access time, but never the full token.

POST /api/v1/bi/embeds

Create an embeddable dashboard, report, saved SQL query, visual query, or metric. The full embed token is returned once; store it securely and use the returned embed_url in an iframe or server-side proxy.

Request
{
  "name": "Partner KPI embed",
  "resource_type": "visual_query",
  "resource_config": {
    "dataset": "pageviews",
    "dimensions": ["path"],
    "metrics": ["count"],
    "limit": 25
  },
  "allowed_origins": ["https://app.example.com"],
  "theme": { "brand": "Acme", "primary_color": "#2563eb" },
  "expires_at": "2026-12-31T23:59:59Z"
}
GET /api/v1/bi/embeds/{id}

Fetch one embed. Use PUT /api/v1/bi/embeds/{id} to update resource settings, origin allowlists, theme, status, or expiration; use DELETE /api/v1/bi/embeds/{id} to revoke it.

POST /api/v1/bi/embeds/{id}/rotate-token

Rotate an embed token. The new full token is returned once, and the previous token immediately stops resolving.

GET /api/embed/bi/{token}

Public embed resolver. Pulse verifies the token, expiration, active status, and request origin, increments access counters, and returns the embed definition plus its resource and optional query result.

GET /api/v1/bi/row-policies

List row-level permission policies. Active policies are enforced on visual queries and drill-through queries for matching datasets.

POST /api/v1/bi/row-policies

Create a row policy using an approved dataset field and operator: eq, neq, in, or not_in.

Request
{
  "name": "US and Canada only",
  "dataset": "pageviews",
  "field": "country",
  "operator": "in",
  "values": ["US", "CA"],
  "is_active": true
}
PUT /api/v1/bi/row-policies/{id}

Update a row policy. Use DELETE /api/v1/bi/row-policies/{id} to remove it.

POST /api/v1/bi/sql

Run ad hoc read-only SQL. SQL must start with SELECT or WITH, cannot include mutating keywords, and must include {{project_id}} so Pulse can enforce tenant scoping.

Request
{
  "sql_text": "SELECT path, COUNT(*) FROM pageviews WHERE project_id = {{project_id}} GROUP BY path ORDER BY 2 DESC",
  "limit": 100
}
GET /api/v1/bi/sql-queries

List saved SQL queries. Use POST /api/v1/bi/sql-queries to save a validated query, and POST /api/v1/bi/sql-queries/{id}/run to execute it.

POST /api/v1/bi/visual-query

Run a visual query over approved datasets: pageviews, events, sessions, and daily_stats. Provide dimensions, metrics, date range, and limit.

POST /api/v1/bi/drill-through

Return tenant-scoped detail rows behind an aggregate chart. Supports approved datasets and whitelisted filters for pageviews, events, sessions, daily_stats, and csv_uploads; each run is stored as drill_through query history.

Request
{
  "dataset": "events",
  "filters": {
    "event_name": "signup",
    "path": ["/pricing", "/signup"]
  },
  "start_at": "2026-05-01T00:00:00Z",
  "end_at": "2026-05-09T23:59:59Z",
  "limit": 100
}
GET /api/v1/bi/query-runs

List SQL, saved SQL, visual, drill-through, and external SQL executions with status, row count, duration, and error details.

GET /api/v1/bi/csv-uploads

List CSV uploads. Use POST /api/v1/bi/csv-uploads with columns and JSON object rows to store external tabular data.

GET /api/v1/bi/csv-uploads/{id}/rows

Page through uploaded CSV rows. Use DELETE /api/v1/bi/csv-uploads/{id} to remove an upload and its rows.

AI Analytics

Requires the ai_queries module. Pulse includes a deterministic query assistant that maps natural-language questions to safe built-in analytics queries, stores query runs, generates heuristic insights, and tracks LLM traces, generations, evals, cost, latency, and token usage.

POST /api/v1/ai/query

Ask a natural-language analytics question. Supported intents include overview, top pages, referrers, events, devices, geography, traffic trend, and errors.

Request
{
  "question": "What changed in traffic this month?",
  "start_at": "2026-05-01T00:00:00Z",
  "end_at": "2026-05-09T23:59:59Z",
  "limit": 10
}
Response 200
{
  "id": "6e4f28b4-5e1a-4c1d-9df0-6b2e3b2587c1",
  "intent": "overview",
  "answer": "Pulse saw 12420 pageviews, 4021 visitors, 4580 sessions, and 932 custom events...",
  "result": {
    "stats": {
      "pageviews": 12420,
      "visitors": 4021,
      "pageview_change_percent": 18.4
    }
  },
  "insights": [
    {
      "title": "Traffic is concentrated",
      "severity": "info",
      "metric": "pages"
    }
  ]
}
GET /api/v1/ai/insights

Generate heuristic insights for a date range, including traffic spikes/drops, elevated bounce rate, and concentrated page traffic. Supports start_at and end_at.

GET /api/v1/ai/history

List stored AI query runs. Supports limit and offset.

LLM Traces, Generations, and Evals

POST /api/v1/ai/llm/traces

Create or update an LLM trace for an agent run, workflow, chat, or generation chain. Use GET /api/v1/ai/llm/traces to list recent traces.

Request
{
  "trace_key": "checkout-agent-2026-05-09T12:00:00Z",
  "name": "Checkout support agent",
  "user_id": "user_123",
  "metadata": { "workflow": "checkout_support" },
  "status": "started"
}
GET /api/v1/ai/llm/traces/{id}

Fetch one stored LLM trace by id.

POST /api/v1/ai/llm/generations

Record one LLM generation with provider/model, token usage, latency, cost, prompt/output payloads, status, and metadata. Use GET /api/v1/ai/llm/generations to list recent generations.

Request
{
  "trace_key": "checkout-agent-2026-05-09T12:00:00Z",
  "provider": "openai",
  "model": "gpt-4.1-mini",
  "operation": "chat_completion",
  "input_tokens": 812,
  "output_tokens": 146,
  "latency_ms": 1180,
  "cost_usd": 0.0042,
  "status": "success"
}
POST /api/v1/ai/llm/evaluations

Record an eval result for a generation or trace, including evaluator name, metric, score, label, pass/fail, and metadata. Use GET /api/v1/ai/llm/evaluations to list recent evals.

GET /api/v1/ai/llm/stats

Return LLM generation volume, error count, token usage, average latency, total cost, eval count, and eval pass rate for a date range.

Web Vitals

Requires the webvitals module. Monitor Core Web Vitals performance across your site with percentile breakdowns.

GET /api/v1/webvitals

Summary of all Web Vitals with p50, p75, and p99 percentiles.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/webvitals?start_at=2026-03-01&end_at=2026-03-22"
Response 200
{
  "vitals": {
    "LCP": { "p50": 1240, "p75": 2180, "p99": 5420, "rating": "good" },
    "FCP": { "p50": 820, "p75": 1450, "p99": 3890, "rating": "good" },
    "CLS": { "p50": 0.04, "p75": 0.09, "p99": 0.32, "rating": "good" },
    "INP": { "p50": 120, "p75": 198, "p99": 512, "rating": "good" },
    "TTFB": { "p50": 280, "p75": 520, "p99": 1890, "rating": "good" }
  }
}
GET /api/v1/webvitals/pages

Web Vitals broken down by page path.

Response 200
{
  "data": [
    {
      "path": "/",
      "LCP_p75": 1890,
      "FCP_p75": 1120,
      "CLS_p75": 0.05,
      "INP_p75": 145,
      "TTFB_p75": 380,
      "samples": 1842
    },
    {
      "path": "/dashboard",
      "LCP_p75": 2890,
      "FCP_p75": 1680,
      "CLS_p75": 0.12,
      "INP_p75": 234,
      "TTFB_p75": 620,
      "samples": 567
    }
  ]
}
GET /api/v1/webvitals/timeseries

Daily p75 values for a specific Web Vital metric.

ParameterTypeDescription
metricstringRequired. One of: LCP, FCP, CLS, INP, TTFB.
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/webvitals/timeseries?metric=LCP&start_at=2026-03-18&end_at=2026-03-22"
Response 200
{
  "metric": "LCP",
  "data": [
    { "date": "2026-03-18", "p75": 2120 },
    { "date": "2026-03-19", "p75": 2045 },
    { "date": "2026-03-20", "p75": 2310 },
    { "date": "2026-03-21", "p75": 1980 },
    { "date": "2026-03-22", "p75": 2180 }
  ]
}

Error & Log Analytics

Requires the error_tracking and logs modules. Capture JavaScript errors, group them by stable fingerprint, attach releases and environments, register source-map artifacts, and query application logs.

GET /api/v1/errors

Get grouped error fingerprints ranked by occurrence count. Groups include release, environment, latest path/browser, and whether a matching source-map artifact exists.

Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/errors?start_at=2026-03-01&end_at=2026-03-22&limit=5"
Response 200
{
  "data": [
    {
      "fingerprint": "1d5b154e8fb6d613a52bc2f6e8bf6bc5",
      "message": "TypeError: Cannot read properties of null (reading 'id')",
      "count": 142,
      "affected_visitors": 98,
      "release": "2026.05.09",
      "environment": "production",
      "source_map_configured": true,
      "last_seen": "2026-03-22T11:42:00Z",
      "first_seen": "2026-03-05T14:23:00Z"
    }
  ]
}
GET /api/v1/errors/detail

Get individual error instances for a specific error message or fingerprint.

ParameterTypeDescription
messagestringError message to look up.
fingerprintstringError fingerprint to look up. Either message or fingerprint is required.
limitintegerNumber of instances to return. Default: 20.
Response 200
{
  "data": [
    {
      "created_at": "2026-03-22T11:42:00Z",
      "path": "/dashboard",
      "filename": "https://acme.com/assets/app.js",
      "lineno": 142,
      "colno": 18,
      "stack": "TypeError: Cannot read properties of null\n    at handleClick (app.js:142:18)",
      "browser": "Chrome 122",
      "os": "Windows 11",
      "release": "2026.05.09",
      "matched_source_map": {
        "minified_url": "https://acme.com/assets/app.js",
        "source_map_url": "s3://artifacts/app.js.map"
      }
    }
  ]
}
GET /api/v1/errors/timeseries

Daily error count over time.

Response 200
{
  "data": [
    { "date": "2026-03-20", "count": 34 },
    { "date": "2026-03-21", "count": 28 },
    { "date": "2026-03-22", "count": 19 }
  ]
}
GET /api/v1/errors/stats

Aggregate error statistics for the period.

Response 200
{
  "total_errors": 342,
  "unique_errors": 18,
  "affected_visitors": 215,
  "releases": [
    { "release": "2026.05.09", "count": 188 }
  ]
}
GET /api/v1/releases

List application releases used for release-aware error and log filtering. Supports limit and offset.

POST /api/v1/releases

Create or update a release record.

Request
{
  "version": "2026.05.09",
  "environment": "production",
  "commit_sha": "9f4c2a1",
  "deployed_at": "2026-05-09T18:00:00Z",
  "metadata": { "service": "web" }
}
DELETE /api/v1/releases/{id}

Delete a release record. Source-map records remain available but are detached from the release row.

GET /api/v1/source-maps

List source-map artifact metadata. Filter by release_version when needed.

POST /api/v1/source-maps

Register a source-map artifact for a minified JavaScript URL and release. Error detail responses surface the matching artifact when the filename and release match.

Request
{
  "release_version": "2026.05.09",
  "environment": "production",
  "minified_url": "https://acme.com/assets/app.js",
  "source_map_url": "s3://artifacts/app.js.map",
  "artifacts": { "debug_id": "web-20260509" }
}
DELETE /api/v1/source-maps/{id}

Delete source-map artifact metadata.

GET /api/v1/logs

List application logs. Supports start_at, end_at, level, release, environment, search, limit, and offset.

GET /api/v1/logs/stats

Get log totals grouped by level and release for the requested date range.

Session Replay

Requires the session_replay module. The browser SDK records sampled, privacy-masked interaction events and stores them by session for later playback.

GET /api/v1/session-replay

List recorded sessions. Supports start_at, end_at, limit, and offset.

Response 200
{
  "data": [
    {
      "id": "rec-f4a7c2",
      "visitor_id": "v_7fz9k2",
      "events_count": 84,
      "entry_page": "/pricing",
      "duration_ms": 128000,
      "is_complete": true
    }
  ]
}
GET /api/v1/session-replay/{id}

Fetch a recording with its ordered event stream for playback.

Heatmaps

Requires the heatmaps module. Visualize where users click, label selectors as semantic events, evaluate those labels retroactively, and detect rage-click friction from repeated clicks on the same selector.

GET /api/v1/heatmaps

Get click coordinate data for a specific page.

ParameterTypeDescription
pathstringRequired. Page path to get heatmap data for.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  "https://pulse.ayushojha.com/api/v1/heatmaps?path=/pricing"
Response 200
{
  "data": [
    { "x": 482, "y": 320, "element_selector": "button.cta-primary", "count": 234 },
    { "x": 720, "y": 580, "element_selector": "a.plan-enterprise", "count": 156 },
    { "x": 240, "y": 580, "element_selector": "a.plan-starter", "count": 98 }
  ]
}
GET /api/v1/heatmaps/stats

Pages ranked by total click count.

Response 200
{
  "data": [
    { "path": "/", "total_clicks": 3421 },
    { "path": "/pricing", "total_clicks": 892 },
    { "path": "/docs", "total_clicks": 567 }
  ]
}

Visual Event Labels

GET /api/v1/heatmaps/labels

List visual event labels that map a page pattern and element selector to a semantic event name.

POST /api/v1/heatmaps/labels

Create a visual label for retroactive event definition from historical click data.

Request
{
  "name": "Pricing hero CTA",
  "event_name": "pricing_hero_cta_click",
  "path_pattern": "/pricing*",
  "element_selector": "button.cta-primary",
  "properties": { "area": "hero" },
  "status": "active"
}
GET /api/v1/heatmaps/labels/stats

Evaluate all active visual labels against historical clicks. Supports start_at, end_at, and limit.

PUT /api/v1/heatmaps/labels/{id}

Update a visual label. Use DELETE /api/v1/heatmaps/labels/{id} to remove it.

GET /api/v1/heatmaps/labels/{id}/stats

Evaluate one visual label against historical clicks for a date range.

GET /api/v1/heatmaps/friction

Detect rage-click clusters from click events. A signal is returned when a visitor clicks the same page and selector at least three times inside a five-second window. Supports optional path, start_at, end_at, and limit.

Feature Flags / Remote Config

Requires the feature_flags module. Create typed feature flags and remote config entries with targeting rules, stable percentage rollouts, optional weighted variants, guardrail metadata, and experiment-backed assignments.

Targeting Rules

Targeting rules use match (all or any) plus conditions against visitor_id, user_id, traits.*, or arbitrary context.* fields. Supported operators are exists, not_exists, eq, neq, contains, starts_with, ends_with, gt, gte, lt, lte, and in.

GET /api/v1/feature-flags

List feature flags for the project.

POST /api/v1/feature-flags

Create a feature flag. Use experiment_id to tie evaluation to an existing running experiment assignment.

Request
{
  "key": "checkout_redesign",
  "name": "Checkout Redesign",
  "enabled": true,
  "flag_type": "boolean",
  "default_value": false,
  "rollout_percentage": 25,
  "targeting_rules": {
    "match": "all",
    "conditions": [
      { "field": "traits.plan", "op": "in", "value": ["pro", "business"] },
      { "field": "context.country", "op": "eq", "value": "US" }
    ]
  },
  "guardrail_metrics": ["checkout_error_rate", "purchase_conversion"]
}
GET /api/v1/feature-flags/{id}

Fetch one feature flag.

PUT /api/v1/feature-flags/{id}

Update a feature flag definition, rollout, targeting rules, experiment binding, or guardrail metadata.

DELETE /api/v1/feature-flags/{id}

Delete a feature flag and its evaluation history.

POST /api/v1/feature-flags/{key}/evaluate

Evaluate a flag for a visitor. Evaluation is stable for percentage rollouts and weighted variants, and each evaluation is recorded.

Request
{
  "visitor_id": "v_7fz9k2",
  "user_id": "user_123",
  "traits": { "plan": "pro" },
  "context": { "country": "US", "device": "desktop" }
}
Response 200
{
  "key": "checkout_redesign",
  "enabled": true,
  "matched": true,
  "variant": null,
  "value": true,
  "reason": "match"
}
GET /api/v1/feature-flags/{id}/evaluations

List recent flag evaluations. Supports limit and offset.

Remote Config

GET /api/v1/remote-config

List remote config entries.

POST /api/v1/remote-config

Create a remote config value with optional targeting.

Request
{
  "key": "checkout_copy",
  "value": { "headline": "Finish your secure checkout" },
  "targeting_rules": { "match": "all", "conditions": [] },
  "is_active": true
}
GET /api/v1/remote-config/{id}

Fetch one remote config entry.

PUT /api/v1/remote-config/{id}

Update a remote config value, targeting rules, or active state.

DELETE /api/v1/remote-config/{id}

Delete a remote config entry.

POST /api/v1/remote-config/{key}/evaluate

Evaluate a remote config entry for a visitor and return the value when targeting matches.

A/B Testing

Requires the ab_testing module. Create and manage experiments with multiple variants, assign visitors, and measure results against goals with lift, confidence, p-values, and significant winner detection.

GET /api/v1/experiments

List all experiments.

Response 200
{
  "data": [
    {
      "id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
      "name": "CTA Button Color",
      "status": "running",
      "variants": 3,
      "created_at": "2026-03-10T09:00:00Z"
    }
  ]
}
POST /api/v1/experiments

Create a new experiment. Variant weights should sum to 100.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CTA Button Color",
    "description": "Test whether blue, green, or orange CTA converts best",
    "variants": [
      {"name": "control_blue", "weight": 34},
      {"name": "variant_green", "weight": 33},
      {"name": "variant_orange", "weight": 33}
    ],
    "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012"
  }' \
  https://pulse.ayushojha.com/api/v1/experiments
Response 201
{
  "id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
  "name": "CTA Button Color",
  "status": "draft",
  "variants": [
    { "name": "control_blue", "weight": 34 },
    { "name": "variant_green", "weight": 33 },
    { "name": "variant_orange", "weight": 33 }
  ],
  "goal_id": "g9a8b7c6-5432-1fed-cba0-987654321012",
  "created_at": "2026-03-22T14:30:00Z"
}
GET /api/v1/experiments/{id}

Get a specific experiment and its configuration.

PUT /api/v1/experiments/{id}/status

Update experiment status. Valid transitions: draftrunningpausedrunningcompleted.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{"status": "running"}' \
  https://pulse.ayushojha.com/api/v1/experiments/exp-a1b2c3d4-5678-9012-efab-cd3456789012/status
DELETE /api/v1/experiments/{id}

Delete an experiment. Only allowed for draft or completed experiments.

GET /api/v1/experiments/{id}/results

Get experiment results with per-variant conversion data, lift versus baseline, p-values, confidence, and significant winner detection. The first configured variant is used as the baseline.

Response 200
{
  "experiment_id": "exp-a1b2c3d4-5678-9012-efab-cd3456789012",
  "baseline_variant": "control_blue",
  "winner": "variant_green",
  "variants": [
    {
      "name": "control_blue",
      "assignments": 1102,
      "conversions": 34,
      "conversion_rate": 3.09,
      "lift_percent": null,
      "p_value": null,
      "confidence": null,
      "significant": false,
      "is_baseline": true
    },
    {
      "name": "variant_green",
      "assignments": 1067,
      "conversions": 41,
      "conversion_rate": 3.84,
      "lift_percent": 24.3,
      "p_value": 0.041,
      "confidence": 95.9,
      "significant": true,
      "is_baseline": false
    }
  ]
}
POST /api/v1/experiments/{id}/assign

Assign a visitor to a variant. Uses stable weighted assignment and returns the same variant for repeat calls with the same visitor.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  -H "Content-Type: application/json" \
  -d '{ "visitor_id": "v_7fz9k2" }' \
  https://pulse.ayushojha.com/api/v1/experiments/exp-a1b2c3d4-5678-9012-efab-cd3456789012/assign
Response 200
{
  "variant": "variant_green"
}

Surveys

Requires the surveys module. Build in-app surveys and adoption guides with customizable triggers, appearance, targeting, and analytics. Collect feedback, NPS, sentiment, tour completion, tooltip engagement, and onboarding outcomes.

GET /api/v1/surveys

List all surveys for the project.

Response 200
{
  "data": [
    {
      "id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
      "name": "NPS Survey",
      "status": "active",
      "responses": 142,
      "created_at": "2026-03-01T10:00:00Z"
    }
  ]
}
POST /api/v1/surveys

Create a new survey with questions, trigger configuration, and appearance settings.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "NPS Survey",
    "questions": [
      {
        "id": "q1",
        "type": "rating",
        "text": "How likely are you to recommend us?",
        "min": 0,
        "max": 10
      },
      {
        "id": "q2",
        "type": "text",
        "text": "What could we improve?",
        "required": false
      }
    ],
    "trigger_config": {
      "type": "time_on_page",
      "delay_seconds": 30,
      "pages": ["/dashboard", "/app/*"],
      "show_once": true
    },
    "appearance": {
      "position": "bottom-right",
      "theme": "dark",
      "primary_color": "#6366f1"
    },
    "response_limit": 500
  }' \
  https://pulse.ayushojha.com/api/v1/surveys
GET /api/v1/surveys/{id}

Get a specific survey with its full configuration.

PUT /api/v1/surveys/{id}

Update a survey’s configuration. Only allowed for draft or paused surveys.

DELETE /api/v1/surveys/{id}

Delete a survey and all its responses. Returns 204.

PUT /api/v1/surveys/{id}/status

Update survey status. Valid values: draft, active, paused, archived.

Request
curl -X PUT \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}' \
  https://pulse.ayushojha.com/api/v1/surveys/srv-b2c3d4e5-6789-0abc-def1-234567890abc/status
GET /api/v1/surveys/{id}/responses

Get individual responses for a survey.

Response 200
{
  "data": [
    {
      "id": "resp-1234",
      "answers": [
        { "question_id": "q1", "value": 9 },
        { "question_id": "q2", "value": "Love the dashboard!" }
      ],
      "completed": true,
      "path": "/dashboard",
      "submitted_at": "2026-03-22T14:12:00Z"
    }
  ],
  "total": 142
}
GET /api/v1/surveys/{id}/stats

Aggregated response statistics.

Response 200
{
  "survey_id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
  "total_responses": 142,
  "completed_responses": 128,
  "completion_rate": 90.1,
  "questions": [
    {
      "id": "q1",
      "text": "How likely are you to recommend us?",
      "avg_value": 8.3,
      "distribution": { "0-6": 12, "7-8": 38, "9-10": 92 }
    }
  ]
}
GET /api/v1/surveys/{id}/nps

Calculate NPS from completed responses. Optionally pass question_id; otherwise Pulse uses the first numeric answer in the 0-10 range.

GET /api/v1/surveys/{id}/sentiment

Run deterministic sentiment scoring over text answers. Optionally pass question_id to target a specific open-text question.

GET /api/v1/surveys/active

Get all active surveys for the frontend. Used by the tracking script to display surveys to visitors. Requires an ingest-scoped key.

Request
curl -H "X-Pulse-Key: pa_live_8B57VGhehP5C2R8rnP535YYs" \
  "https://pulse.ayushojha.com/api/v1/surveys/active"
Response 200
{
  "data": [
    {
      "id": "srv-b2c3d4e5-6789-0abc-def1-234567890abc",
      "name": "NPS Survey",
      "questions": [ /* ... */ ],
      "trigger_config": { /* ... */ },
      "appearance": { /* ... */ }
    }
  ]
}

Guides, Tooltips, and Tours

GET /api/v1/guides

List in-app guides, onboarding flows, product tours, tooltips, announcements, and checklists.

POST /api/v1/guides

Create a guide with ordered steps, targeting rules, visual appearance, and priority.

Request
{
  "name": "New dashboard tour",
  "guide_type": "tour",
  "steps": [
    { "id": "welcome", "title": "Welcome", "body": "Start here." },
    { "id": "filters", "selector": "[data-tour='filters']", "body": "Filter your analysis." }
  ],
  "targeting": { "paths": ["/dashboard"], "segments": ["new_users"] },
  "appearance": { "theme": "dark", "position": "bottom-right" },
  "priority": 10
}
GET /api/v1/guides/active

Return active guides ordered by priority. Frontend renderers can apply the returned targeting and appearance metadata.

GET /api/v1/guides/{id}

Fetch one guide. Use PUT /api/v1/guides/{id} to update it or DELETE /api/v1/guides/{id} to remove it.

PUT /api/v1/guides/{id}/status

Set guide status to draft, active, paused, or archived.

POST /api/v1/guides/{id}/events

Record guide events such as shown, started, step_viewed, completed, dismissed, and converted. Use GET /api/v1/guides/{id}/events for visitor event history.

GET /api/v1/guides/{id}/stats

Return shown, started, completed, dismissed, converted, completion-rate, and dismissal-rate metrics.

CSV Exports

Requires the exports module. Download analytics data as CSV files for use in spreadsheets or data pipelines.

GET /api/v1/exports/{type}

Export data as a CSV file. Returns a file download with Content-Disposition header.

ParameterTypeDescription
typestring (path)Required. One of: stats, pages, referrers, events, devices, geo, campaigns.
start_atstringPeriod start (ISO 8601).
end_atstringPeriod end (ISO 8601).
Request
curl -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -o pages_export.csv \
  "https://pulse.ayushojha.com/api/v1/exports/pages?start_at=2026-03-01&end_at=2026-03-22"
Response Headers
Content-Type: text/csv
Content-Disposition: attachment; filename="pages_2026-03-01_2026-03-22.csv"
Example CSV Output
path,views,unique_views,avg_duration
/,4821,2190,45.2
/pricing,2314,1820,92.7
/docs/getting-started,1892,1456,214.3
/blog/analytics-guide,1203,987,312.1
/signup,891,834,67.8

Integrations

Requires the integrations module. The integrations catalog lists available and planned source, destination, SDK, framework, BI, and marketing integrations with capabilities and setup metadata.

GET /api/v1/integrations

List integrations. Supports category, capability, and status filters.

GET /api/v1/integrations/{key}

Fetch one integration by key, including its capabilities and setup metadata.

Sources

Requires the sources module. Sources register server-side or external systems that send JSON events into Pulse through source-specific tokens. Accepted source events are audited and routed to matching destinations.

GET /api/v1/sources

List event sources for the project. Responses include the source token prefix but never the full token.

POST /api/v1/sources

Create an event source. The full source token is returned once in the creation response.

Request
{
  "name": "Stripe",
  "source_type": "stripe.webhook",
  "description": "Billing and subscription webhooks",
  "schema": { "required": ["id", "type"] },
  "config": { "environment": "production" },
  "is_active": true
}
GET /api/v1/sources/{id}

Fetch one source. Use PUT /api/v1/sources/{id} to update metadata or active status, or DELETE /api/v1/sources/{id} to remove it.

GET /api/v1/sources/{id}/ingestions

List recent source ingestion attempts with payload, sanitized headers, event type, destination delivery count, and received timestamp. Supports limit and offset.

POST /api/source/{id}/collect

Public source ingestion endpoint. Authenticate with X-Pulse-Source-Token or Authorization: Bearer .... Pulse derives the event type from event_type, type, event, or name, then routes the normalized event to destinations.

Request
curl -X POST "https://pulse.ayushojha.com/api/source/7c2f0f06-9df7-48f0-a91c-4db0e0574cc3/collect" \
  -H "Content-Type: application/json" \
  -H "X-Pulse-Source-Token: psrc_..." \
  -d '{"event_type":"checkout.session.completed","customer_id":"cus_123","amount":4900}'

Destinations

Requires the destinations module. Destinations route collected events to external systems through a reliable outbox with retries, dead-letter status, signed webhook delivery, and health reporting.

GET /api/v1/destinations

List event destinations for the project.

POST /api/v1/destinations

Create a webhook destination. Leave event_types empty to receive every collected event type. Use transform to shape the payload stored in the delivery outbox for this destination.

Request
{
  "name": "Warehouse ingest",
  "destination_type": "webhook",
  "endpoint_url": "https://warehouse.example.com/pulse/events",
  "secret": "whsec_...",
  "headers": { "X-Team": "growth" },
  "event_types": ["pageview", "event", "identify"],
  "transform": {
    "include": ["event_type", "payload.customer_id", "payload.amount"],
    "rename": { "payload.customer_id": "customer.id" },
    "drop_fields": ["payload.card_token"],
    "static_fields": { "source": "pulse" },
    "wrap": "data"
  },
  "is_active": true
}
Transform keyDescription
include / include_fieldsWhitelist dot-path fields from the original payload.
exclude / drop_fieldsRemove dot-path fields from the destination payload.
rename / rename_fieldsMove fields from old dot paths to new dot paths.
set / static_fieldsAdd static values at dot-path keys.
wrap / wrap_keyWrap the transformed payload under one top-level key.
GET /api/v1/destinations/{id}

Fetch one destination. Use PUT /api/v1/destinations/{id} to update it, or DELETE /api/v1/destinations/{id} to remove it.

GET /api/v1/destination-deliveries

List outbox deliveries. Supports status, limit, and offset. Status values are pending, retry, delivered, and dead_letter.

POST /api/v1/destination-deliveries/{id}/retry

Move a failed or dead-letter delivery back to the pending queue.

GET /api/v1/destination-health

Return per-destination delivery counts, failure timestamps, and health status.

Shared Dashboards

Requires the sharing module. Create password-protected, time-limited dashboard links that can be shared with stakeholders without giving them API access.

POST /api/v1/sharing

Create a new shared dashboard link.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q1 Marketing Report",
    "modules": ["pageviews", "referrers", "utm", "geo"],
    "password": "stakeholder2026",
    "expires_at": "2026-04-30T23:59:59Z"
  }' \
  https://pulse.ayushojha.com/api/v1/sharing
Response 201
{
  "id": "shr-e4f5a6b7-8901-2345-cdef-678901234567",
  "name": "Q1 Marketing Report",
  "url": "https://pulse.ayushojha.com/shared/shr-e4f5a6b7",
  "modules": ["pageviews", "referrers", "utm", "geo"],
  "password_protected": true,
  "expires_at": "2026-04-30T23:59:59Z",
  "created_at": "2026-03-22T15:00:00Z"
}
GET /api/v1/sharing

List all shared dashboard links for the project.

DELETE /api/v1/sharing/{id}

Revoke a shared dashboard link. The URL will immediately stop working. Returns 204.

Governance

Requires the governance module. Governance lets teams define tracking plans, approve event schemas, validate required properties and types at ingest time, maintain a data dictionary, and monitor instrumentation health.

Tracking Plans

A project can have active tracking plans in observe mode, which records violations without blocking ingestion, or reject mode, which rejects invalid custom events after logging the validation issue.

GET /api/v1/governance/tracking-plans

List tracking plans for the project.

POST /api/v1/governance/tracking-plans

Create a tracking plan. enforcement_mode must be observe or reject.

Request
{
  "name": "Production Tracking Plan",
  "description": "Approved production web events",
  "enforcement_mode": "observe",
  "is_active": true
}
GET /api/v1/governance/tracking-plans/{id}

Fetch one tracking plan.

PUT /api/v1/governance/tracking-plans/{id}

Update a tracking plan’s name, description, enforcement mode, or active state.

DELETE /api/v1/governance/tracking-plans/{id}

Delete a tracking plan and its plan-scoped schemas.

Event Schemas

Event schemas define the approved event catalog. property_schema is a JSON object where each key is a property path and each value is either a type string or an object with type, required, and optional descriptive metadata. Supported types are string, number, integer, boolean, object, array, timestamp, and any.

GET /api/v1/governance/event-schemas

List event schemas. Optionally filter with tracking_plan_id.

POST /api/v1/governance/event-schemas

Create an event schema. Status values are draft, approved, and deprecated.

Request
{
  "tracking_plan_id": "b8f7de26-79b7-4d59-9cd0-7d1a2fe034a2",
  "event_name": "purchase",
  "description": "Completed checkout event",
  "status": "approved",
  "required_properties": ["amount", "currency"],
  "property_schema": {
    "amount": "number",
    "currency": { "type": "string", "required": true },
    "metadata.coupon": "string"
  }
}
GET /api/v1/governance/event-schemas/{id}

Fetch one event schema.

PUT /api/v1/governance/event-schemas/{id}

Update an event schema definition.

PUT /api/v1/governance/event-schemas/{id}/status

Move an event schema through the approval workflow by setting draft, approved, or deprecated.

DELETE /api/v1/governance/event-schemas/{id}

Delete an event schema.

Data Dictionary

GET /api/v1/governance/data-dictionary

List data dictionary entries. Optionally filter by entry_type: event, property, metric, or dimension.

POST /api/v1/governance/data-dictionary

Create a data dictionary entry.

Request
{
  "entry_type": "property",
  "name": "purchase.amount",
  "data_type": "number",
  "description": "Checkout total before tax",
  "owner": "growth",
  "is_pii": false
}
PUT /api/v1/governance/data-dictionary/{id}

Update a data dictionary entry.

DELETE /api/v1/governance/data-dictionary/{id}

Delete a data dictionary entry.

Quality Monitoring

GET /api/v1/governance/violations

List ingestion quality violations. Supports event_name, violation_type, limit, and offset.

GET /api/v1/governance/health

Summarize instrumentation health for the last 24 hours, including event coverage and violation counts.

Response 200
{
  "status": "warning",
  "observed_events_24h": 14,
  "covered_events_24h": 12,
  "coverage_ratio": 0.86,
  "violations_24h": 7,
  "top_violations": [
    { "event_name": "purchase", "violation_type": "missing_required_property", "count": 3 }
  ]
}

Privacy / Audit

Admin-scoped endpoints for runtime privacy settings, DSAR export/delete workflows, and audit trail review. Ingestion can anonymize IP addresses before GeoIP lookup, strip city/region precision, respect DNT and Sec-GPC, filter bots, and require explicit consent modes.

GET /api/v1/privacy/settings

Return project privacy controls. Defaults enable IP anonymization, DNT/Sec-GPC handling, and bot filtering.

PUT /api/v1/privacy/settings

Update privacy controls. Supported fields are anonymize_ip, respect_dnt, bot_filtering, consent_required, allowed_consent_modes, and blocked_user_agents.

Request
{
  "anonymize_ip": true,
  "respect_dnt": true,
  "bot_filtering": true,
  "consent_required": true,
  "allowed_consent_modes": ["analytics", "measurement"],
  "blocked_user_agents": ["synthetic-monitor"]
}
GET /api/v1/privacy/users/{visitor_id}/export

Export identity, sessions, pageviews, events, surveys, experiments, replay recordings, and telemetry for one visitor.

DELETE /api/v1/privacy/users/{visitor_id}

Delete stored visitor data across analytics tables and write an audit log entry.

GET /api/v1/audit-logs

List project audit logs. Supports limit and offset.

Email Reports

Requires the email_reports module. Configure scheduled daily, weekly, or monthly digests. Delivery is handled by posting a normalized email payload to EMAIL_REPORT_WEBHOOK_URL.

GET /api/v1/email-reports

List report configurations and whether delivery is configured.

POST /api/v1/email-reports

Create a scheduled report.

Request
{
  "name": "Weekly Growth",
  "recipients": ["team@example.com"],
  "schedule": "weekly",
  "modules": ["core", "goals"],
  "is_active": true
}
PUT /api/v1/email-reports/{id}

Update report recipients, schedule, modules, or active state.

POST /api/v1/email-reports/{id}/test

Send a test report immediately through the configured delivery webhook.

DELETE /api/v1/email-reports/{id}

Delete a report configuration. Returns 204.

Alerts

Requires the alerts module. Set up threshold-based alert rules that notify webhook channels when metrics cross specified boundaries.

GET /api/v1/alerts

List all alert rules for the project.

Response 200
{
  "data": [
    {
      "id": "alt-c3d4e5f6-7890-1234-abcd-ef0123456789",
      "name": "Traffic Spike Alert",
      "project_id": "a7b8c9d0-1234-5678-9abc-def012345678",
      "module": "traffic",
      "metric": "visitors",
      "operator": "gt",
      "threshold": 500,
      "window_minutes": 60,
      "cooldown_minutes": 360,
      "notify_channels": [
        {"type": "webhook", "url": "https://hooks.example.com/pulse"}
      ],
      "is_active": true,
      "last_triggered_at": "2026-03-20T14:30:00Z"
    }
  ]
}
POST /api/v1/alerts

Create a new alert rule.

FieldTypeDescription
namestringHuman-readable name for the alert.
modulestringLabel for the area being monitored (e.g., traffic, error_tracking).
metricstringSupported values: pageviews, visitors, bounce_rate, error_count, avg_duration.
operatorstringComparison: gt, lt, gte, lte, eq.
thresholdnumberValue to compare against.
window_minutesintegerRolling window size in minutes.
cooldown_minutesintegerMinimum time between repeated alerts.
notify_channelsarrayWebhook targets. Each item supports type: "webhook", url, and optional secret for HMAC signatures.
Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Error Spike Alert",
    "module": "error_tracking",
    "metric": "error_count",
    "operator": "gt",
    "threshold": 50,
    "window_minutes": 15,
    "cooldown_minutes": 60,
    "notify_channels": [
      {"type": "webhook", "url": "https://hooks.slack.com/services/T00/B00/xxx", "secret": "optional-signing-secret"}
    ]
  }' \
  https://pulse.ayushojha.com/api/v1/alerts
Alert webhooks: When an alert fires, Pulse sends an alert_triggered JSON payload to each webhook channel. If secret is set, the request includes an X-Pulse-Signature HMAC-SHA256 signature over the raw JSON body.
Webhook Payload
{
  "event": "alert_triggered",
  "project_id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "triggered_at": "2026-05-09T18:22:14Z",
  "metric_value": 73,
  "alert": {
    "id": "alt-c3d4e5f6-7890-1234-abcd-ef0123456789",
    "name": "Error Spike Alert",
    "module": "error_tracking",
    "metric": "error_count",
    "operator": "gt",
    "threshold": 50,
    "window_minutes": 15,
    "cooldown_minutes": 60
  }
}
PUT /api/v1/alerts/{id}

Update an alert rule’s configuration.

DELETE /api/v1/alerts/{id}

Delete an alert rule. Returns 204.

POST /api/v1/alerts/{id}/toggle

Enable or disable an alert rule without deleting it.

Request
curl -X POST \
  -H "X-Pulse-Key: pa_live_wYxrlD97_Oy1UpLAyi_TwPTG" \
  https://pulse.ayushojha.com/api/v1/alerts/alt-c3d4e5f6-7890-1234-abcd-ef0123456789/toggle
Response 200
{
  "id": "alt-c3d4e5f6-7890-1234-abcd-ef0123456789",
  "name": "Error Spike Alert",
  "module": "error_tracking",
  "metric": "error_count",
  "is_active": false,
  "updated_at": "2026-05-09T18:23:01Z"
}

Admin API

Administrative endpoints for managing projects and API keys. All admin endpoints require the admin bearer token.

Admin-only: These endpoints require Authorization: Bearer <PULSE_ADMIN_TOKEN>. Do not expose the admin token in frontend code.
POST /api/admin/projects

Register a new project.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My SaaS App",
    "domain": "app.example.com"
  }' \
  https://pulse.ayushojha.com/api/admin/projects
Response 201
{
  "id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "name": "My SaaS App",
  "domain": "app.example.com",
  "created_at": "2026-03-22T16:00:00Z"
}
GET /api/admin/projects

List all registered projects.

Response 200
{
  "data": [
    {
      "id": "a7b8c9d0-1234-5678-9abc-def012345678",
      "name": "My SaaS App",
      "domain": "app.example.com",
      "created_at": "2026-03-22T16:00:00Z"
    }
  ]
}
GET /api/admin/projects/{id}

Get details for a specific project.

POST /api/admin/projects/{id}/keys

Create an API key for a project.

Request — Ingest Key
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Ingest",
    "scopes": ["ingest"],
    "expires_at": "2027-01-01T00:00:00Z",
    "allowed_modules": null
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/keys
Response 201
{
  "id": "key-f1e2d3c4-b5a6-9870-fedc-ba0987654321",
  "key": "pa_live_xK9mP2vQr7nL4wJ8bY6tZ3sA",
  "name": "Production Ingest",
  "scopes": ["ingest"],
  "allowed_modules": null,
  "expires_at": "2027-01-01T00:00:00Z",
  "created_at": "2026-03-22T16:05:00Z"
}
Important: The full key value is only returned once at creation time. Store it securely — it cannot be retrieved later.
GET /api/admin/projects/{id}/keys

List all API keys for a project. Key values are masked.

Response 200
{
  "data": [
    {
      "id": "key-f1e2d3c4-b5a6-9870-fedc-ba0987654321",
      "name": "Production Ingest",
      "key_prefix": "pa_live_xK9m...",
      "scopes": ["ingest"],
      "allowed_modules": null,
      "expires_at": "2027-01-01T00:00:00Z",
      "created_at": "2026-03-22T16:05:00Z",
      "last_used_at": "2026-03-22T16:42:00Z"
    }
  ]
}
DELETE /api/admin/projects/{id}/keys/{key_id}

Revoke an API key. The key immediately stops working. Returns 204.

Webhooks

Configure webhook integrations to receive real-time notifications when specific events occur in your analytics data.

POST /api/admin/projects/{id}/webhooks

Create a new webhook subscription.

FieldTypeDescription
urlstringRequired. HTTPS endpoint to receive webhook payloads.
eventsarrayRequired. Event types to subscribe to.
secretstringShared secret for HMAC-SHA256 signature verification.

Supported event types:

EventDescription
traffic_spikeTriggered when active visitors exceed 2x the rolling average.
zero_trafficTriggered when no pageviews are recorded for 30+ minutes.
daily_summarySent daily at midnight UTC with the day’s aggregate stats.
Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.slack.com/services/T00/B00/xxxxx",
    "events": ["traffic_spike", "zero_traffic"],
    "secret": "whsec_my_signing_secret_2026"
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks
Response 201
{
  "id": "wh-d4e5f6a7-8901-2345-bcde-f01234567890",
  "url": "https://hooks.slack.com/services/T00/B00/xxxxx",
  "events": ["traffic_spike", "zero_traffic"],
  "created_at": "2026-03-22T16:30:00Z"
}
GET /api/admin/projects/{id}/webhooks

List all webhooks for a project.

PUT /api/admin/projects/{id}/webhooks/{webhook_id}

Update a webhook’s URL, events, or secret.

Request
curl -X PUT \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["traffic_spike", "zero_traffic", "daily_summary"]
  }' \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks/wh-d4e5f6a7-8901-2345-bcde-f01234567890
DELETE /api/admin/projects/{id}/webhooks/{webhook_id}

Remove a webhook subscription. Returns 204.

POST /api/admin/projects/{id}/webhooks/{webhook_id}/test

Fire a test payload to the webhook URL to verify connectivity.

Request
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  https://pulse.ayushojha.com/api/admin/projects/a7b8c9d0-1234-5678-9abc-def012345678/webhooks/wh-d4e5f6a7-8901-2345-bcde-f01234567890/test
Response 200
{
  "success": true,
  "status_code": 200,
  "response_time_ms": 142
}

Webhook Payload Format

All webhook payloads follow the same envelope structure:

Webhook Payload Example (traffic_spike)
{
  "event": "traffic_spike",
  "project_id": "a7b8c9d0-1234-5678-9abc-def012345678",
  "timestamp": "2026-03-22T14:30:00Z",
  "data": {
    "active_visitors": 187,
    "rolling_avg": 45,
    "spike_multiplier": 4.16
  }
}
Signature verification: Pulse signs webhook payloads with HMAC-SHA256 using your secret. The signature is sent in the X-Pulse-Signature header. Verify it by computing HMAC-SHA256(secret, raw_body) and comparing.

Rate Limiting

Pulse enforces rate limits per project to ensure fair usage and system stability.

ScopeLimitWindow
Ingest endpoints100 requests/secondPer project
Query endpoints100 requests/secondPer project
Admin endpoints20 requests/secondPer admin token

When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.

Rate Limited Response (429)
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json

{
  "error": "Rate limit exceeded. Retry after 1 second."
}
Tip: The tracking script automatically batches and throttles requests on the client side, so you typically won’t hit rate limits with normal browser traffic. Rate limits are more relevant for server-side API usage.

Error Codes

All API errors follow a consistent JSON format. The HTTP status code indicates the error category.

Error Response Format
{
  "error": "Human-readable error message describing what went wrong."
}

Status Codes

CodeMeaningCommon Causes
200 OK Request succeeded. Response body contains the requested data.
201 Created Resource created successfully (projects, keys, funnels, etc.).
204 No Content Delete operations completed successfully. No response body.
400 Bad Request Invalid JSON, missing required fields, invalid parameter values.
401 Unauthorized Missing or invalid API key / admin token.
403 Forbidden API key lacks the required scope, or the target module is disabled.
404 Not Found The requested resource (project, funnel, goal, etc.) does not exist.
429 Too Many Requests Rate limit exceeded. Check the Retry-After header.
500 Internal Server Error Unexpected server error. Contact support if it persists.

Common Error Examples

Missing API Key
Response 401
{
  "error": "Missing API key. Provide it via X-Pulse-Key header or ?key= query parameter."
}
Invalid API Key
Response 401
{
  "error": "Invalid API key."
}
Insufficient Scope
Response 403
{
  "error": "API key does not have the required 'query' scope for this endpoint."
}
Module Disabled
Response 403
{
  "error": "Module 'funnels' is not enabled for this project."
}
Invalid Request Body
Response 400
{
  "error": "Invalid request body: missing required field 'name'."
}
Resource Not Found
Response 404
{
  "error": "Funnel 'f1a2b3c4-5678-9abc-def0-1234567890ab' not found."
}