Tracking events
FounderPass surfaces analytics — most-viewed deals, claim conversion, partner-level metrics — but only on the events your integration sends. There are two ways to send them:
- Client-side —
POST /events/track, called from the end-user surface (hosted app, iframe, browser). No Bearer token; attribution comes from a Firebase token if present, otherwise asession_id. - Server-side —
POST /v1/events/track, called from your backend with your Partner JWT. Attributes the event to one of your users without Firebase, resolving them byuser_id,email, orexternal_user_id. See Server-side attribution below.
Client-side tracking
Hook a single POST /events/track call into the key moments of the user journey: listing impressions, detail views, instructions views, promo-code copies, and redemptions.
:::info Partner context, not the API JWT
POST /events/track is called from the end-user surface (your hosted app, embed iframe, or API client), not from your backend. Send it to your hosted or sandbox subdomain so the API can identify the partner — no Bearer token required. If the request carries a valid Firebase ID token, the event is attributed to that user; otherwise a stable session_id is the only attribution. To attribute server-side without Firebase, use POST /v1/events/track instead.
:::
Sending an event
curl -X POST https://<your-slug>.perks.founderpass.com/events/track \
-H "Content-Type: application/json" \
-d '{
"event_type": "deal_detail_viewed",
"deal_id": "018e4b2a-0000-7def-abcd-000000000010",
"session_id": "sess_abc123",
"source": "hosted_app",
"metadata": { "referrer": "homepage" }
}'
Success returns 200 { "status": "tracked" }.
| Field | Type | Required | Notes |
|---|---|---|---|
event_type | enum | Yes | See list below |
session_id | string | Yes | A stable per-session ID you generate client-side |
source | enum | Yes | hosted_app or iframe. api is reserved for the server-side endpoint and is rejected here with 400. |
deal_id | uuid | No | Required for deal-scoped events |
user_id | uuid | No | Ignored — server attributes from the Firebase token if present, never from the body |
metadata | object | No | Free-form JSON for your own attribution |
:::warning Don't try to spoof user_id
The server discards user_id from the request body on this public endpoint. Authenticated attribution only happens when the request carries a valid Firebase ID token. For anonymous events, lean on session_id. To attribute an event to a user from your backend (no Firebase), use the server-side endpoint.
:::
Event types
| Value | When to fire |
|---|---|
deal_list_viewed | A deal listing page rendered for the user |
deal_detail_viewed | User opened the deal detail page |
instructions_viewed | User opened the redemption instructions |
redeem_url_clicked | User clicked the redeem CTA |
promo_code_copied | User copied the promo code |
unique_code_claimed | User claimed a one-time code |
deal_redeemed | User completed redemption |
upgrade_cta_clicked | User clicked a tier-upgrade CTA |
deal_inquiry_submitted | User filled the "request access" form |
:::tip deal_redeemed is emitted for you on the API path
When you fetch redemption instructions via GET /v1/deals/{id}/instructions?user_id=…, the server auto-emits a deal_redeemed event for that user — once per (partner, user, deal). You don't need to send deal_redeemed yourself for API-driven redemptions; doing so is a no-op for the count.
:::
Server-side attribution
POST /v1/events/track records an event from your backend and attributes it to one of your users — no Firebase token required. Authenticate with your Partner JWT (Bearer token), and identify the user with exactly one of user_id, email, or external_user_id (tried in that order; the first present wins). The resolved user must belong to your partner.
curl -X POST https://perks.founderpass.com/v1/events/track \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_type": "deal_detail_viewed",
"external_user_id": "cust_abc123",
"deal_id": "018e4b2a-0000-7def-abcd-000000000010",
"metadata": { "surface": "members-dashboard" }
}'
Success returns 200 { "status": "tracked", "user_id": "…" }.
| Field | Type | Required | Notes |
|---|---|---|---|
event_type | enum | Yes | Same event types as the public endpoint |
user_id | uuid | One of these three | A FounderPass user ID |
email | string | One of these three | The user's email, scoped to your partner |
external_user_id | string | One of these three | The ID you set on the user via Syncing users |
deal_id | uuid | For deal_redeemed, yes | Validated against your visible catalogue; required for deal_redeemed |
session_id, metadata | string / object | No | Carried through onto the recorded event |
The event is always recorded with source = "api". Resolution rules:
- If none of
user_id/email/external_user_idis supplied →400. - If the identifier resolves to no user in your partner →
404. - A
deal_idoutside your visible catalogue (hidden,api_excluded, wrong tenant) →404. - A
deal_redeemedis deduplicated to one row per(partner, user, deal), so retries are safe.
Reading analytics back
This endpoint requires a Partner JWT — call it from your backend, not from the browser.
curl "https://perks.founderpass.com/v1/analytics?days=30" \
-H "Authorization: Bearer $TOKEN"
days is a lookback window (1–365, default 30).
Response
{
"total_users": 1240,
"active_users": 312,
"total_deal_views": 5821,
"total_redemptions": 184,
"upgrade_cta_clicks": 27,
"top_deals": [
{ "deal_id": "...", "product_name": "Notion", "views": 312, "redemptions": 41 }
],
"event_counts": {
"deal_list_viewed": 9201,
"deal_detail_viewed": 5821,
"deal_redeemed": 184
}
}
For per-deal time series or CSV export, use the corresponding admin/portal endpoints in the API Reference.