Prevenue Docs

Direct API Installation

Install Prevenue Direct API for backend source-of-truth events.

Direct API is the source-of-truth path for backend events that are confirmed by durable product, billing, setup, or integration state. Use it when the event should be emitted from server code after a database write, provider confirmation, billing update, OAuth completion, background job, or metered usage calculation.

This is different from the Web SDK. The Web SDK is browser-safe supplemental instrumentation for visible user intent and UI friction. Direct API uses a secret ingest key and must never run in browser, mobile, desktop, or public client code.

Create an ingest key

In Prevenue, open Settings > Integrations > Direct API and create a key. The secret is shown once.

Store it only in backend or server environment variables:

SAASFUNNELS_INGEST_API_KEY=<SAASFUNNELS_INGEST_KEY>

Never expose this key in client code, frontend bundles, mobile apps, desktop apps, public repositories, logs, analytics tools, or AI prompts.

Use developer tools

Use SaaSFunnels CLI and SaaSFunnels MCP Server to speed up Direct API setup without exposing real keys.

Validate a payload before shipping it:

saasfunnels events validate ./saasfunnels-event.json --source direct --json

Send one smoke event from a server-safe environment:

SAASFUNNELS_INGEST_API_KEY=<SAASFUNNELS_INGEST_KEY> saasfunnels events send-test --file ./saasfunnels-event.json --json

Install a coding-agent handoff file:

saasfunnels agent install --target codex

Start MCP when an agent needs Prevenue context:

SAASFUNNELS_API_KEY=<DEVELOPER_READ_KEY> saasfunnels mcp serve

MCP is read-only by default. Enable send_test_event only for explicit setup smoke testing.

Send a server event

Send events to:

https://app.prevenue.ai/api/events/ingest

Use the ingest key as a bearer token:

curl -X POST https://app.prevenue.ai/api/events/ingest \
  -H "Authorization: Bearer <SAASFUNNELS_INGEST_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "evt_direct_001",
    "event_name": "usage_limit_hit",
    "account_id": "acct_123",
    "user_id": "user_456",
    "timestamp": "2026-06-20T15:00:00.000Z",
    "semantic_type": "limit_friction",
    "event_kind": "friction_event",
    "sentiment": "negative",
    "sentiment_score": -0.8,
    "sentiment_confidence": 0.9,
    "sentiment_source": "explicit",
    "value": 1,
    "properties": {
      "plan": "starter",
      "limit_name": "monthly_events",
      "usage_ratio": 0.93
    }
  }'

Minimum viable SaaS account facts

Prevenue should learn a small account profile while events arrive. Send these as bounded properties on normal account-scoped events, especially signup, login, activation, plan change, team, and usage rollup events.

FactPreferred field namesNotes
Customer signup dateaccount_created_at, signup_at, or customer_created_atThis is the customer's product signup date, not the time Prevenue first saw the account.
Login activityuser_logged_in or session_started events with semantic_type: "login"Include user_id when a user caused the login. Prevenue can derive login counts from these events.
Plan and lifecycleplan, plan_name, current_plan, account_status, or subscription_statusKeep values bounded and stable, such as starter, growth, active, trialing, or canceled.
Trial stateis_in_trial, trial_started_at, trial_ends_atUse this when Stripe is not connected or before Stripe is available. Stripe is authoritative for billing and trial state when connected.
Team and seatsuser_count, active_user_count, seat_count, or member_countSend current counts from backend state or a scheduled rollup, not raw user lists.

Example login event with account facts:

{
  "event_id": "login:acct_123:user_456:2026-06-20T15:00:00.000Z",
  "event_name": "user_logged_in",
  "account_id": "acct_123",
  "user_id": "user_456",
  "timestamp": "2026-06-20T15:00:00.000Z",
  "semantic_type": "login",
  "event_kind": "login",
  "properties": {
    "account_created_at": "2026-06-01T12:00:00.000Z",
    "plan": "starter",
    "is_in_trial": true,
    "trial_ends_at": "2026-06-28T12:00:00.000Z",
    "user_count": 7,
    "seat_count": 10,
    "active_user_count": 6
  }
}

Refresh these facts on lifecycle changes or daily account rollups. Do not create noisy page-view events just to refresh profile data.

Usage-based and API-heavy products

For API-heavy products, do not forward every customer API request to Prevenue. Send aggregate usage from a backend scheduled job instead.

The recommended unit is one aggregate event per account, product, metric, and closed time bucket:

{
  "event_id": "usage_rollup:acct_123:api_platform:api_calls:2026-06-01",
  "event_name": "usage_rollup_recorded",
  "account_id": "acct_123",
  "timestamp": "2026-06-02T00:10:00.000Z",
  "semantic_type": "usage",
  "event_kind": "product_action",
  "value": 148253,
  "properties": {
    "product_key": "api_platform",
    "metric_key": "api_calls",
    "unit": "request",
    "usage_start": "2026-06-01T00:00:00.000Z",
    "usage_end": "2026-06-02T00:00:00.000Z",
    "aggregation_grain": "day",
    "source_system": "usage_metering"
  }
}

Use a cron job, queue worker, warehouse job, or metering service to calculate these rows from the customer's source-of-truth usage table. Daily buckets are enough for most revenue signals. Hourly buckets are useful when usage changes quickly, a customer is near a limit, or the revenue team needs same-day routing.

The aggregate payload should answer:

QuestionField
Which customer used it?account_id
Which product surface was used?properties.product_key
Which usage meter changed?properties.metric_key
How much usage happened?value
What unit is the value in?properties.unit
Which usage period does it cover?properties.usage_start and properties.usage_end
Can the row be safely retried?Stable event_id

Keep product_key, metric_key, and unit stable. Treat them like a small product and metric catalog. For example, api_platform + api_calls + request should keep the same meaning over time. If pricing or packaging changes, add properties such as plan, tier, or pricing_version; do not silently change the unit behind an existing metric key.

Prevent overload

Aggregate before sending. A customer with millions of API requests should usually send hundreds or thousands of aggregate rows, not millions of Direct API calls. In the standard Direct API path, each request should represent an aggregate row; for very large row counts, use a bulk import plan instead of increasing concurrency.

Use these guardrails:

  • Send only closed buckets, such as the previous hour or previous day.
  • Use deterministic event_id values so retries are treated as the same logical event instead of duplicating usage.
  • Retry failed rows with backoff and the same event_id.
  • Split backfills into small date ranges and send oldest closed periods first.
  • Keep each request compact. The Direct API accepts small JSON event payloads, not raw logs or large arrays.
  • Start scheduled jobs with low concurrency, then increase only after Activity shows healthy accepts and low rejection rates.
  • Send milestone or threshold events separately, such as quota_blocked, limit_hit, overage_started, or plan_upgraded.

If the customer needs to send a large historical backfill or very large numbers of aggregate rows per run, coordinate a bulk import plan instead of increasing request concurrency.

Direct API event discovery

Use Direct API for events that are better known by the backend than the browser:

  • Completed billing or subscription state changes.
  • Checkout completed, invoice paid, payment failed, refund, downgrade completed, cancel completed.
  • Durable setup milestones such as integration configured, OAuth connected, destination configured, API key created, or first event ingested.
  • Metered usage, credits consumed, quota calculations, API calls, background jobs, sync results, and anything billable.
  • first_* milestones that must be race-safe and based on persisted state.

Use the Web SDK instead for browser-only intent before backend state changes, visible prompts, page-level UI friction, and supplemental adoption signals.

When using an AI coding tool, ask it to classify event candidates as:

ClassificationMeaning
direct_api_nowDurable backend event that should be sent through Direct API now
existing_sourceAlready covered by Stripe, PostHog, Segment, Direct API, warehouse, or another reliable event source
web_sdk_follow_upBrowser-visible intent or UI friction that is better captured by the Web SDK

Event contract

Every revenue-relevant Direct API event should include:

FieldRequiredNotes
event_nameYesStatic lower_snake_case name such as usage_limit_hit
account_idYesStable customer, account, workspace, tenant, company, or organization ID
timestampYesISO timestamp for when the product event occurred
event_idRecommendedStable idempotency key for retryable completed events
user_idOptionalActor context when a user caused the event
semantic_typeRecommendedOne of Prevenue's revenue event families
event_kindRecommendedproduct_action, billing_event, friction_event, or another normalized kind
valueOptionalNumeric amount, count, usage value, credit amount, or revenue magnitude
propertiesOptionalBounded structured context

Use stable event_id values for completed state changes, for example checkout_completed:<checkout_id> or integration_connected:<provider>:<account_id>.

Do not emit first_* milestones unless persisted state proves first occurrence race-safely. If first-occurrence detection is approximate, skip the event or use a non-first_* name.

Events to add first

Start with a small allowlist of 3 to 6 high-signal backend events.

Event familyExample namesDirect API guidance
Activation / first valueaccount_activated, first_value_completed, onboarding_completedEmit after persisted state proves completion
High-value usagefeature_used, workflow_completed, report_exported, api_credits_used, generation_completed, sync_completedBest for metered, key-feature, or server-confirmed usage
Limit frictionlimit_hit, quota_blocked, overage_prompt_viewedBest when the backend calculates the limit
Checkoutcheckout_started, checkout_completed, checkout_abandonedDirect API or Stripe should be authoritative for completed checkout
Billing lifecyclesubscription_updated, subscription_upgraded, invoice_payment_failed, payment_succeeded, plan_changedPrefer Stripe when connected; use Direct API only when your backend is the billing source of truth
Downgrade or canceldowngrade_completed, cancel_completed, subscription_pausedUse bounded reason_code; never send free text
Team expansionteam_member_invited, seat_added, role_assignedEmit after invite or seat persistence succeeds
Integration connectedintegration_connected, oauth_connected, destination_configuredEmit after credentials/configuration are persisted
Product or support frictionsync_failed, setup_failed, support_neededUse bounded error codes/statuses only

Sentiment fields

Sentiment is optional and separate from the event family. Missing sentiment is stored as unknown, not neutral; use neutral only when the product signal is explicitly neutral.

Send explicit bounded sentiment from the backend when the source-of-truth system knows it. If sentiment is omitted, Prevenue can infer a reviewable mapping default from the event name, semantic type, event kind, and bounded property keys. Explicit payload sentiment takes precedence over mapped sentiment.

FieldValues
sentimentpositive, neutral, negative, unknown
sentiment_scoreNumber from -1 to 1
sentiment_confidenceNumber from 0 to 1
sentiment_sourceexplicit, inferred, ai, integration, unknown

Do not send free-text feedback, survey responses, chat messages, support transcripts, raw comments, or AI-generated summaries as sentiment context. Send bounded labels and scores only.

See Event KPI Signals for the canonical KPI definitions, evidence bands, and Account filters these fields power.

Data safety

Do not send:

  • Passwords, tokens, cookies, signatures, auth headers, API keys, session values, invite links, webhook URLs, provider tokens, or destination secrets.
  • Raw PII, full URLs with query strings, full request/response bodies, stack traces, raw logs, prompts, comments, notes, support messages, or descriptions.
  • Generic page views, auth page views, modal opens, hovers, local UI toggles, or debug logs as first-batch Direct API events.

Verify setup

Send one account-scoped activation, usage, limit, or setup event from backend code. Then check Settings > Integrations > Direct API and Events in Prevenue.

A successful setup should show:

  • The event is accepted by the Direct API route.
  • The event normalizes successfully.
  • The event attaches to the expected account.
  • Rejected payloads clearly explain missing account_id, auth, or revenue relevance.
  • No secrets, raw PII, query-string values, or full request/response bodies are stored.