# Configuration

URL: https://supapulse.app/docs/revenue/configuration

The settings for revenue tracking, for the Stripe webhook, the HTTP API and the @supapulse/revenue SDK.

## Stripe webhook [#stripe-webhook]

| Setting | Value |
| --- | --- |
| URL | `https://supapulse.app/api/webhooks/stripe/{publicToken}`, shown with your token in the site's settings |
| Events | `checkout.session.completed`, `invoice.paid`, `charge.refunded`, `customer.subscription.updated`, `customer.subscription.deleted` |
| Signing secret | The `whsec_…` secret Stripe shows for the endpoint, saved in the site's settings |
| Signature tolerance | 5 minutes |
| Success | `200` with `{ "received": true }` |

See the [Stripe guide](https://supapulse.app/docs/guides/stripe).

## HTTP API [#http-api]

| Setting | Value |
| --- | --- |
| URL | `https://supapulse.app/api/ingest` |
| Method | `POST` |
| Headers | `Content-Type: application/json`, `X-API-Token: sec_…` |
| Body | `{ "type": "revenue", "data": { … } }` |
| Success | `202 Accepted` with `{ "success": true }` |
| Rate limit | 2,000 requests per hour per IP address |

The token can also go in the body as `"token": "sec_…"` instead of the header. The header wins if both are set.

## Visitor fingerprint endpoint [#visitor-fingerprint-endpoint]

| Setting | Value |
| --- | --- |
| URL | `https://supapulse.app/api/fingerprint` |
| Method | `POST` |
| Auth | The site's token, in `X-API-Token` or a `token` body field. Call it from the browser with the public token. |
| Success | `200` with `{ "visitor_fingerprint": "…" }` |

`getVisitorFingerprint()` in `@supapulse/core` calls it for you and caches the result. The endpoint shares the ingest rate limit.

## @supapulse/revenue options [#supapulserevenue-options]

```ts
const revenue = new Revenue({
  apiToken: process.env.SUPAPULSE_SECRET_TOKEN!, // required
  endpoint: 'https://supapulse.app/api/ingest',
  debug: false,
  maxRetries: 3,
  retryBackoff: 1000,
});
```

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `apiToken` | `string` | none | The secret token. The constructor throws without it. |
| `endpoint` | `string` | `https://supapulse.app/api/ingest` | Where events are sent. |
| `debug` | `boolean` | `false` | Logs sends and retries with a `[Supapulse Revenue]` prefix. |
| `maxRetries` | `number` | `3` | Attempts per event for network errors and `429`. |
| `retryBackoff` | `number` | `1000` | Base delay in milliseconds; doubles after each failed attempt. A `429` waits for `Retry-After` instead. |

Any other `4xx` (`401`, `402`, `403`, `422`) isn't retried: `track()` throws on the first one.

## Environment variables [#environment-variables]

| Variable | Where | Value |
| --- | --- | --- |
| `SUPAPULSE_SECRET_TOKEN` | Server only | `sec_…` |
| `NEXT_PUBLIC_SUPAPULSE_TOKEN` (or your framework's public prefix) | Browser | `pub_…` |

Use one Supapulse site per environment if you want test payments kept out of production numbers.

## Serverless and edge runtimes [#serverless-and-edge-runtimes]

The HTTP API is one `fetch` call, so it runs anywhere `fetch` does: Node.js, Vercel and Netlify functions, AWS Lambda, Cloudflare Workers, Deno and Bun. Await the call before the function returns, or the runtime may freeze it mid-request.

## Security checklist [#security-checklist]

- The secret token lives only in server environment variables and your secrets manager.
- Webhook signatures are verified before any revenue is sent.
- Revenue is sent only for payments that succeeded, and refunds are sent as refunds.
- The secret token is regenerated after anyone with access leaves, or after a leak.
