supapulsedocs
Revenue packageInstallation

Installation

Set up revenue tracking. Identify customers in the browser, connect Stripe or get the secret token, and choose between the HTTP API and the @supapulse/revenue SDK.

1. Identify customers in the browser

Install the browser tracker and call identify() once the user is known, after login or signup:

supapulse.identify(user.id); // window.supapulse with the script tag

In React, use useIdentify(). identify() never counts toward your event usage.

2. Pick how payments arrive

  • Stripe: open the site's settings, find the Revenue section, and follow the Stripe guide. You don't need the secret token.
  • Anything else: continue below.

3. Get the secret token

Open the site's settings in the dashboard and regenerate the secret token. It's shown once, so copy it then. It starts with sec_.

Regenerating replaces the old token right away. If a server is already using it, update that server at the same time.

.env
SUPAPULSE_SECRET_TOKEN=sec_your_secret_token

Don't prefix it with NEXT_PUBLIC_, VITE_ or anything else that bundles it into browser code.

4. Send revenue

@supapulse/revenue SDK

npm install @supapulse/revenue
lib/supapulse.ts
import { Revenue } from '@supapulse/revenue';

export const revenue = new Revenue({ apiToken: process.env.SUPAPULSE_SECRET_TOKEN! });

await revenue.track({
  customer_id: 'user_123',
  amount: 49.9,
  currency: 'USD',
  transaction_id: 'in_1Q8x…',
  event_type: 'renewal',
});

track() validates its input, retries network errors and 429 responses, and throws straight away on any other 4xx. It warns in the console if it's loaded in a browser. The full surface is in the Revenue API reference.

HTTP API

No package needed. Any language that can make an HTTPS request works:

lib/supapulse.ts
type Revenue = {
  customerId: string;
  amount: number;
  transactionId: string;
  eventType?: 'purchase' | 'subscription' | 'renewal' | 'upgrade' | 'downgrade' | 'churn' | 'refund';
  currency?: string;
  visitorFingerprint?: string;
  sessionId?: string;
  paymentProcessor?: string;
  subscriptionId?: string;
  mrrImpact?: number;
  occurredAt?: string;
};

export async function trackRevenue(revenue: Revenue): Promise<void> {
  const response = await fetch('https://supapulse.app/api/ingest', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Token': process.env.SUPAPULSE_SECRET_TOKEN!,
    },
    body: JSON.stringify({
      type: 'revenue',
      data: {
        customer_id: revenue.customerId,
        amount: revenue.amount,
        transaction_id: revenue.transactionId,
        event_type: revenue.eventType,
        currency: revenue.currency ?? 'USD',
        visitor_fingerprint: revenue.visitorFingerprint,
        session_id: revenue.sessionId,
        payment_processor: revenue.paymentProcessor,
        subscription_id: revenue.subscriptionId,
        mrr_impact: revenue.mrrImpact,
        occurred_at: revenue.occurredAt,
      },
    }),
  });

  if (!response.ok) {
    throw new Error(`Supapulse ingest failed: ${response.status} ${await response.text()}`);
  }
}

JSON.stringify drops undefined fields, so optional values can be left out. The rest of these docs use this trackRevenue() helper.

5. Check it

Send a test payment through your provider's test mode, then open the site's revenue view. The payment shows up once the worker has processed it, usually within seconds.

If nothing appears:

  • 401 Invalid API token: the token is wrong or was regenerated.
  • 402: the site's team has no active plan. The body's code is plan_inactive.
  • 422: the body is missing customer_id, amount or transaction_id, or a field is invalid. The response lists each field. A public token also gets 422, because it can't send revenue.
  • 202 but no new revenue: a payment with the same transaction_id and event_type was already stored.