# Growth Rail Docs — Full Documentation
> Growth Rail is a referral and rewards platform. These docs cover the guides, first-party SDKs (JavaScript, React, React Native, iOS, Android), auth and purchase integrations, and the REST API for building referral programs into your web and mobile applications.
Source: https://docs.growthrail.dev • OpenAPI: https://api.growthrail.dev/openapi/public.json
This file concatenates every documentation page. Each page below is delimited by a horizontal rule and its source URL.
---
# Growth Rail — Overview
Everything you need to build powerful referral and rewards programs into your application — from a 15-minute quick start to the full REST API.
Growth Rail is a platform for adding referral and rewards programs to your web and mobile applications. It handles referral tracking, dynamic reward logic, signed webhooks, multi-project management, drop-in UI, and first-party SDKs.
## Core Capabilities
- **Referral Tracking** — Automated tracking of invitations and successful signups across multiple campaigns. Unique 6-character referral codes, cookie-based attribution, and deferred deep linking for mobile.
- **Dynamic Rewards** — Grant rewards based on custom trigger events like signups, purchases, or KYC completion. Built-in eligibility checks prevent fraud and double-claiming.
- **Webhooks** — Real-time delivery of reward events to your backend via signed webhooks. Automatic retries (5 attempts), delivery logs, and manual replay from the dashboard.
- **Multi-Project** — Manage multiple apps or environments under one organization. Isolated data per project with separate secrets, users, campaigns, and webhooks.
- **Built-in UI** — Drop-in referral sharing UI with floating trigger buttons, customizable modals/drawers, social sharing, and new-user promotional banners. Fully themeable.
- **SDK Support** — First-party SDKs for React, React Native, and vanilla JavaScript. Wrap your app in a provider and get referral functionality with a few lines of code.
## How It Works
Growth Rail handles the complete referral lifecycle in four steps:
1. **User Shares** — Existing user gets a unique referral link.
2. **Friend Clicks** — SDK auto-detects and tracks the referral.
3. **Action Taken** — Friend completes signup or qualifying event.
4. **Reward Sent** — Webhook notifies your backend to credit the referrer.
## Where to go next
- **Quick Start** — Get up and running with a complete referral flow in under 15 minutes.
- **API Reference** — Explore the REST API endpoints, schemas, and return types.
- **Core Concepts** — Understand how projects, users, and rewards fit together (see the Referral Flow guide).
---
# Quick Start
Integrate your first referral program in under 15 minutes. This guide covers installation, user initialization, referral tracking, and reward delivery.
> **Note:** **Prerequisites:** You need a Growth Rail account and a project created in the dashboard. Grab your **Project Secret** from Settings and configure your **Referral Redirect Link**.
## What You'll Build
By the end of this guide, your app will have a complete referral flow:
1. **Install** — Add SDK packages
2. **Initialize** — Wrap app with provider
3. **Identify** — Register users
4. **Track** — Track reward events
5. **Deliver** — Configure webhooks
### 1. Install the SDK
**React**
```bash
npm install @growth-rail/react @growth-rail/core
```
**React Native**
```bash
npm install @growth-rail/react-native @growth-rail/core
```
**Vanilla JS**
```bash
npm install @growth-rail/core
```
### 2. Initialize the SDK
Add the Growth Rail provider at the root of your application. Pass your **Project Secret** as the `projectSecretKey` prop.
**React**
```tsx
import { GrowthRailProvider } from '@growth-rail/react';
function App() {
return (
);
}
```
**Vanilla JS**
```typescript
import { GrowthRail } from '@growth-rail/core';
GrowthRail.init({
projectSecretKey: process.env.GROWTH_RAIL_SECRET,
autoPageTrack: true,
debug: true,
});
```
### 3. Identify users after login
Call `initAppUser()` once the user is authenticated. This creates the user in Growth Rail (if they don't exist), generates a unique **referral code**, and returns a shareable **referral link**.
**React**
```tsx
import { useGrowthRail } from '@growth-rail/react';
export function DashboardPage() {
const { initAppUser } = useGrowthRail();
const { user } = useAuth();
useEffect(() => {
if (user?.id) initAppUser(user.id);
}, [user?.id]);
return
Welcome to your dashboard
;
}
```
**Vanilla JS**
```typescript
await GrowthRail.initAppUser(session.userId);
```
### 4. Track reward events
When a referred user completes a qualifying action (signup, purchase, etc.), track the reward event via the backend REST API (`POST /api/v1/sdk/track-reward-event`) or automatically via native Stripe & RevenueCat webhooks/integrations.
```bash
POST https://api.growthrail.dev/api/v1/sdk/track-reward-event
Header: x-project-secret-key: sk_live_xxx
{
"newUserId": "user_123",
"referralTrackingId": "tr_789",
"eventName": "user_signup",
"environment": "Production"
}
```
> **Note:** **Automatic detection:** When a user lands on your app via a referral link (`?referralCode=ABC123`), the SDK automatically calls `trackReferral()` and stores the tracking ID in a cookie. You track the reward event from your backend server or integration when the qualifying action occurs.
### 5. Configure a webhook
In the [Growth Rail dashboard](https://app.growthrail.dev/dashboard/your-project-id/integration?tab=webhooks), go to **Integrations → Reward Webhooks** and add your backend URL. When a reward is triggered, Growth Rail POSTs a signed JSON payload to your endpoint.
For webhook payload details, authentication, retry behavior, and event types, see the [Webhooks guide](/guides/webhooks).
## Bonus: Show the Referral UI
Growth Rail comes with a built-in referral sharing UI — a floating trigger button and a modal/drawer with the user's referral link, copy button, and social sharing options. The SDK renders this automatically based on your Campaign settings.
**React**
```tsx
const { showReferralDashboard, showFloatingButton } = useGrowthRail();
showFloatingButton({ position: 'bottom-right' });
```
**Vanilla JS**
```typescript
GrowthRail.createTriggerButton({ position: 'bottom-right', displayMode: 'floating' });
GrowthRail.showReferralDashboard();
```
## What's Next
### Project Setup
Configure redirect links, project secrets, and multi-environment strategies.
### Referral Flow
Deep-dive into the full referral lifecycle from link creation to reward delivery.
### Reward Logic
Understand trigger events, eligibility checks, and webhook routing per rule.
---
# Project Setup
A Project is the top-level container for your Growth Rail integration. Everything — users, campaigns, reward rules, and webhooks — belongs to a project. This guide walks you through creating, configuring, and securing your project.
> **Note:** **Prerequisites:** You need a Growth Rail account. If you haven't signed up yet, create an account at the Growth Rail dashboard. Once logged in, the onboarding wizard will guide you through organization and project creation.
## Architecture Overview
Growth Rail uses a hierarchical structure to organize your data. Understanding this hierarchy is key to setting up your integration correctly.
Organization (Your company or team account) → Project (One per app or environment) → Campaign (Referral UI & experience config) → Users (App users with referral codes)
| Entity | Scope | Description |
| --- | --- | --- |
| **Organization** | Account-wide | Your top-level account. Manages billing, team members, and projects. All projects live under one org. |
| **Project** | Per app / environment | An isolated container with its own secret, users, campaigns, and webhooks. Create one per app or per environment (dev/staging/prod). |
| **Campaign** | Per project | Defines the referral UI experience — trigger button, sharing modal, and new-user welcome banner. |
| **App Users** | Per project | Your end users, each with a unique referral code. Isolated per project — a user in "Production" is separate from a user in "Staging". |
## Creating a Project
You can create a project during onboarding or at any time from the dashboard.
### 1. Choose a platform type
Select whether your project targets **Web App** or **Mobile App**. This choice determines which configuration options appear (e.g., mobile projects get App Store / Play Store URL fields). The platform type **cannot be changed** after creation.
#### Web App
For websites and web apps. Referral links redirect to your configured URL. The SDK uses cookies for tracking and renders UI components in the browser DOM.
#### Mobile App
For React Native apps. Supports deep linking, install referrer tracking, and probabilistic device fingerprint matching for deferred deep links.
### 2. Name and describe your project
Give your project a clear, descriptive name. If you run multiple environments, include the environment in the name to avoid confusion.
| Field | Type | Description |
| --- | --- | --- |
| `name` | string | A human-readable name (e.g. "MyApp - Production"). Required. |
| `description` | string | Optional. A short description to help identify the project's purpose. |
| `uniqueId` | string | Auto-generated identifier (e.g. `proj_k8xn2m`). Used in all REST API paths. |
| `appType` | `"Web"` \| `"Mobile"` | Platform type. Set during creation and cannot be changed. |
### 3. Get your Project Secret
After creation, your project is assigned a **Project Secret** (prefixed with `sk_`). This is the credential your SDK uses on every request. You'll find it under **Project Settings** in the dashboard.
```bash
# Your project secret looks like this:
sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
```
## Project Secret
The **Project Secret** is the credential the SDK sends on every API request in the `X-GrowthRail-ProjectSecret` header. Growth Rail uses it to identify which project the request belongs to and to verify the request origin.
**Environment Variable**
```bash
# .env (recommended approach)
GROWTH_RAIL_PROJECT_SECRET=sk_a1b2c3d4e5f6g7h8i9j0...
# Access in your app
process.env.GROWTH_RAIL_PROJECT_SECRET
```
**React**
```tsx
```
**Vanilla JS**
```typescript
import { GrowthRail } from '@growth-rail/core';
GrowthRail.init({
projectSecretKey: 'sk_a1b2c3d4e5f6g7h8i9j0...',
debug: true,
});
```
> **Danger:** **Keep your Project Secret safe.** Always load it from an environment variable — never hardcode it in source files or share it in Slack messages.
## REST API Authentication
For server-to-server calls to the Growth Rail REST API, use an **API Key** (prefixed `grak_`). Create one in the dashboard under **Organisation → API Keys**.
```bash
curl -X POST https://api.growthrail.dev/api/v1/public/projects/{projectId}/referrals/track \
-H "Authorization: Bearer grak_your_api_key" \
-H "Content-Type: application/json" \
-d '{"referralCode": "ABC123"}'
```
See the [Authentication](/authentication) guide for the full breakdown of Project Secret (SDK) vs API Key (REST API).
## Referral Redirect Link
The **Referral Redirect Link** is the base URL that Growth Rail appends referral parameters to when generating shareable links for your users. Set this in the dashboard under **Campaign settings**.
This URL should point to your **signup or landing page** — the page where new users land when following a referral link. When the SDK initializes on that page, it automatically detects the `referralCode` query parameter and calls `trackReferral()` behind the scenes.
- Points to your signup or landing page
- Uses HTTPS in production
- The SDK is initialized on this page (so it can detect the referral code)
- Do not add query parameters to the base URL — Growth Rail appends them automatically
## Mobile App Configuration
If your project type is **Mobile**, you'll need to configure additional app details for deep linking and store redirects. Navigate to **Project Settings** and fill in your app information.
| Field | Description | Example |
| --- | --- | --- |
| `appName` | Your app's display name | MyApp |
| `appStoreUrl` | iOS App Store listing URL | `https://apps.apple.com/app/id123456` |
| `playStoreUrl` | Google Play Store listing URL | `https://play.google.com/store/apps/details?id=com.yourapp` |
| `iconUrl` | App icon (max 10 MB, drag-and-drop upload) | Used in referral link previews |
## SDK Installation
With your project configured, install the Growth Rail SDK in your application.
**React**
```bash
npm install @growth-rail/react @growth-rail/core
```
**React Native**
```bash
npm install @growth-rail/react-native @growth-rail/core
```
**Vanilla JS**
```bash
npm install @growth-rail/core
```
### Initialize the SDK
**React**
```tsx
import { GrowthRailProvider } from '@growth-rail/react';
function App() {
return (
);
}
```
**React Native**
```tsx
import { GrowthRailProvider } from '@growth-rail/react-native';
export default function App() {
return (
);
}
```
**Vanilla JS**
```typescript
import { GrowthRail } from '@growth-rail/core';
GrowthRail.init({
projectSecretKey: 'sk_your_project_secret',
cookieDomain: '.yourapp.com',
autoPageTrack: true,
debug: true,
});
const user = await GrowthRail.initAppUser('user_123');
```
## Configuration Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | — | **Required.** Your project's secret key (starts with `sk_`). |
| `userId` | string? | `undefined` | Your internal user ID. If provided, auto-calls `initAppUser()`. |
| `cookieDomain` | string? | Current domain | Domain for tracking cookies. Use `.yourapp.com` for cross-subdomain attribution. |
| `autoPageTrack` | boolean? | `false` | Automatically detect `referralCode` in URL and track referral on page load. |
| `debug` | boolean? | `false` | Enable verbose console logging for debugging. Disable in production. |
## Multiple Environments
The recommended pattern is to create **one project per environment**. Each project has its own secret, campaigns, and user data — ensuring test data never bleeds into production.
| Project Name | Purpose |
| --- | --- |
| MyApp - Development | Local testing and feature development |
| MyApp - Staging | Pre-production QA and integration testing |
| MyApp - Production | Live traffic with real users |
## Setup Verification Checklist
- **Project created** — visible in the dashboard with a unique ID
- **Project Secret copied** — stored securely in environment variables
- **Referral Redirect Link set** — points to your signup or landing page
- **SDK installed** — package added and provider/init configured
- **Debug mode enabled in dev** — check console for Growth Rail initialization logs
## What's Next
### Referral Flow
Learn the complete lifecycle from link generation to reward delivery.
### Campaign Setup
Configure the referral UI — trigger button, modal, and new-user banner.
### Webhooks
Set up real-time reward event delivery to your backend.
---
# Referral Flow
Understand the complete lifecycle of a referral — from generating a shareable link to delivering a reward to the referrer's backend.
## Overview
A Growth Rail referral has three participants and four stages:
- **Referrer** — Existing user shares their link
- **Click** — Referee clicks the referral link
- **Action** — Referee completes the qualifying event
- **Reward** — Webhook fires to your backend
## Stage 1 — Generate the Referral Link
Every user gets a unique 6-character alphanumeric referral code (e.g. `ABC123`). When you call `initAppUser()`, Growth Rail automatically creates this code and returns a ready-to-share referral link.
**React**
```tsx
import { useGrowthRail } from '@growth-rail/react';
function InvitePage() {
const { initAppUser } = useGrowthRail();
const [referralLink, setReferralLink] = useState('');
useEffect(() => {
initAppUser(currentUserId).then(user => setReferralLink(user.referralLink));
}, []);
return (
);
}
```
**Vanilla JS**
```typescript
await GrowthRail.initAppUser('user_123');
const link = GrowthRail.getReferralLink();
// Or show the built-in sharing modal
GrowthRail.showReferralDashboard();
```
The link format is controlled by your project's **Referral Redirect Link** setting:
```text
{referralRedirectLink}?referralCode={code}
{referralRedirectLink}?referralCode={code}&rewardEventName={eventName}
```
| Parameter | Required | Description |
| --- | --- | --- |
| `referralCode` | Yes | The referrer's unique code. The SDK reads this automatically on page load. |
| `rewardEventName` | No | Pre-assigns the qualifying event for this referral. When set, only a `trackRewardEvent(eventName)` call with the matching name can claim the reward. Omit to allow any reward event to claim it. |
### Built-in sharing UI
The SDK includes a complete referral sharing UI with copy-to-clipboard, social sharing buttons (Twitter, Facebook, LinkedIn, WhatsApp, Email), and customizable branding. The modal/drawer appearance is configured in your Campaign settings.
Show it with `showReferralDashboard()` or let the floating trigger button handle it automatically.
## Stage 2 — The Referee Clicks the Link
When a new user (the *referee*) visits your app via a referral link, the SDK detects the `referralCode` query parameter automatically during initialization and calls `trackReferral()` behind the scenes.
This creates a **Referral Tracking Item** with status `Pending` and persists two cookies for later attribution. No code needed on your side — the SDK handles it automatically on every page load.
| Response Field | Description |
| --- | --- |
| `referralTrackingId` | UUID of the pending tracking record. Saved to the `gr_tracked_referral` cookie and used when `trackRewardEvent()` is called. |
| `promotionalText` | Promotional text to display to the referred user (set in Campaign settings). Used by the new-user banner. Empty string if not configured. |
> **Cookie-based tracking:** The SDK writes two cookies, each with a 30-day expiry — `gr_ref_code` (the referral code) and `gr_tracked_referral` (the tracking ID). This means a user can close the browser, return days later, complete signup, and the referral attribution still works.
## Stage 3 — The Referee Completes the Qualifying Action
After the referee signs up (or completes any other qualifying event you configured), track the event via the backend REST API (`POST /api/v1/sdk/track-reward-event`) or let native Stripe & RevenueCat integrations sync automatically. This triggers the reward evaluation.
**REST API** — `POST /api/v1/sdk/track-reward-event`
```json
// Request (sent from backend with x-project-secret-key header)
{
"newUserId": "user_bob",
"referralTrackingId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventName": "user_signup",
"environment": "Production"
}
// Response
{
"rewardClaimed": true
}
```
### What happens under the hood
- Growth Rail looks up the tracking record by `referralTrackingId`
- Validates the tracking status is `Pending` (not already completed)
- Checks for duplicate referrer/referee combinations
- Updates status to `Completed` and marks reward as claimed
- Creates a reward history entry for the referrer
- Fires all configured webhooks for the campaign
## Stage 4 — Reward Delivery via Webhook
When the reward event is validated and the tracking item moves to `Completed`, Growth Rail immediately fires a webhook POST to every URL configured for the campaign.
Your backend receives a signed JSON payload containing the referrer, referee, event name, and tracking ID. Use the tracking ID as an idempotency key to safely deliver the reward (credits, discount codes, premium access, etc.) to the referrer.
For the full payload structure, authentication, retry behavior, and event types, see the [Webhooks guide](/guides/webhooks).
## Referral Status Lifecycle
| Status | Meaning | Transitions To |
| --- | --- | --- |
| Pending | Referee clicked the link; awaiting the qualifying action. | Completed, Failed |
| Completed | Qualifying action done; reward webhook fired. Terminal state. | — |
| Failed | Eligibility check failed (duplicate, expired, etc.). | — |
## The New User Banner
When a referee lands on your app via a referral link, the SDK can automatically display a personalized promotional banner (e.g. "You were invited by Alice — sign up for a special welcome bonus!").
Configure the banner text and position in the Campaign settings. The SDK renders it automatically when `referralCode` is detected in the URL. No extra code needed.
| Position | Description |
| --- | --- |
| `top-center` | Full-width banner at the top of the viewport |
| `bottom-center` | Full-width banner at the bottom |
| `top-left` | Toast notification in the top-left corner |
| `top-right` | Toast notification in the top-right corner |
| `bottom-left` | Toast notification in the bottom-left corner |
| `bottom-right` | Toast notification in the bottom-right corner |
## Mobile Deep Linking
For mobile apps, Growth Rail supports **deferred deep linking**. When a user clicks a referral link but doesn't have the app installed:
1. The user is redirected to the App Store or Play Store
2. After installing, the user opens the app
3. The SDK uses **probabilistic device fingerprint matching** to connect the original click to the install
4. The referral is attributed and tracking continues normally
The matching considers device characteristics (OS, screen size, timezone, locale, IP address) within a **2-hour attribution window**.
---
# Reward Logic
Reward events define the conditions under which a referrer earns a reward. Configure trigger events in the dashboard, and Growth Rail handles validation, deduplication, and webhook delivery automatically.
## How Rewards Work
The reward system connects three pieces: a **trigger event** (what the referee does), a **tracking record** (the referral attribution), and a **webhook** (how you get notified). When all three align, the reward is granted.
Trigger Event (Referee completes an action) → Validation (Eligibility checks pass) → Grant (Status updated to Completed) → Webhook (Your backend is notified)
## Trigger Events
A `triggerEvent` is a string you define — Growth Rail makes no assumptions about what it means. You pass the same string when calling `/api/v1/sdk/track-reward-event` from your backend server or integration webhook. The event name must match exactly (case-sensitive).
```json
POST /api/v1/sdk/track-reward-event
Header: x-project-secret-key: sk_live_xxx
{
"newUserId": "user_123",
"referralTrackingId": "tr_789",
"eventName": "user_signup",
"environment": "Production"
}
```
### Common Event Patterns
| Event Name | When to Fire | Use Case |
| --- | --- | --- |
| `user_signup` | After the referee completes registration | Most common. Simple "refer a friend" programs. |
| `email_verified` | After the referee verifies their email | Higher quality leads. Reduces fake signups. |
| `first_purchase` | After the referee makes their first purchase | E-commerce referral programs with revenue requirement. |
| `plan_upgraded` | After the referee upgrades to a paid plan | SaaS products. Reward only for paying customers. |
| `kyc_completed` | After identity verification passes | Fintech and regulated industries. |
| `trial_started` | After the referee starts a free trial | SaaS with trial-based conversion funnels. |
> **Multiple events:** You can track multiple reward events for the same referral. Each event is tracked independently.
## Eligibility Checks
Before granting a reward, Growth Rail performs several checks to ensure the reward is legitimate. All checks must pass for the reward to be granted.
| Check | What It Does |
| --- | --- |
| **Tracking status** | The referral tracking item must be in `Pending` status |
| **No duplicate claim** | The reward cannot be claimed twice for the same tracking record |
| **Unique referee** | The same referee can't be linked to the same referrer more than once |
| **Event match** | If the tracking was linked to a specific event, the event name must match |
> **Fraud protection built-in:** These checks protect against self-referrals, duplicate accounts, and reward farming. If any check fails, no webhook is fired.
## Tracking Reward Events
### Server-Side REST API (`/api/v1/sdk/track-reward-event`)
Reward events are tracked server-side (or automatically synced via native Stripe and RevenueCat integrations):
```bash
curl -X POST https://api.growthrail.dev/api/v1/sdk/track-reward-event \
-H "x-project-secret-key: sk_live_your_project_secret" \
-H "Content-Type: application/json" \
-d '{
"newUserId": "user_123",
"referralTrackingId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventName": "first_purchase",
}'
```
## Reward Delivery
When a reward is granted, Growth Rail fires all enabled webhooks for the campaign. Your backend receives a JSON payload with everything needed to identify and credit the referrer.
For the full webhook payload, authentication, retry behavior, and delivery logs, see the [Webhooks guide](/guides/webhooks).
## Complete Reward Setup
1. **Configure the campaign** — In the dashboard under **Campaign**, set up the referrer experience (trigger button, modal text) and the new user experience (welcome banner).
2. **Create a webhook** — Under **Integrations → Reward Webhooks**, add your backend endpoint URL and a secret token for verification. See the [Webhooks guide](/guides/webhooks).
3. **Track the event from your server or webhook**
```bash
POST /api/v1/sdk/track-reward-event
{ "newUserId": "user_123", "referralTrackingId": "tr_789", "eventName": "user_signup" }
```
4. **Handle the webhook in your backend** — When the webhook fires, verify the secret and use `referrerId` and `trackingId` from the payload to credit your user in your system.
---
# Webhooks
Webhooks deliver real-time reward events from Growth Rail to your backend. When a referral reward is granted, Growth Rail POSTs a signed JSON payload to your configured endpoint so you can credit users, send notifications, or trigger any downstream action.
## How Webhooks Work
1. **Reward Granted** — Eligibility checks pass
2. **Webhook Queued** — Payload built and enqueued
3. **POST Sent** — Signed request to your endpoint
4. **Logged** — Result stored for debugging
## Creating a Webhook
In the [Growth Rail dashboard](https://app.growthrail.dev/dashboard/your-project-id/integration?tab=webhooks), go to **Integrations → Reward Webhooks**, then click **Add Webhook**.
| Field | Required | Description |
| --- | --- | --- |
| `name` | Yes | A descriptive label (e.g. "Credits Service", "Notification Handler"). |
| `url` | Yes | The HTTPS endpoint on your server that receives POST requests. |
| `secret` | Yes | A shared secret sent as a Bearer token in the `Authorization` header. |
| `enabled` | No | Whether this webhook is active. Defaults to `true`. Disabled webhooks receive no events. |
## Environment routing
Each reward webhook is tagged with an **environment scope**:
| Scope | Receives conversions from |
| --- | --- |
| `Sandbox` | Stripe/RevenueCat sandbox inbound webhooks, or SDK calls with `environment: "Sandbox"` |
| `Production` | Stripe/RevenueCat production inbound webhooks, or SDK calls with `environment: "Production"` |
| `Both` | Any sandbox **or** production conversion (one endpoint for all environments) |
Stripe and RevenueCat conversions are routed automatically from the inbound webhook token. The backend API `/api/v1/sdk/track-reward-event` accepts an optional `environment` (`Sandbox` | `Production`); if omitted, **all** configured webhooks receive the event once (preserves legacy fan-out).
## Webhook Payload
Growth Rail sends a `POST` request with `Content-Type: application/json` and your secret in the `Authorization: Bearer` header.
| Header | Value |
| --- | --- |
| `X-GrowthRail-Delivery-Id` | Stable delivery identifier for deduplication |
| `Authorization` | `Bearer ` |
```json
{
"event": "user_signup",
"referrerId": "user_alice",
"refereeId": "user_bob",
"rewardClaimedAt": "2025-03-01T10:00:00.000Z",
"trackingId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"rewardStatus": "Completed",
"projectId": "proj_k8xn2m",
"projectName": "MyApp Production",
"campaignId": "cam_r3f9t2"
}
```
| Field | Type | Description |
| --- | --- | --- |
| `event` | string | The trigger event name that caused the reward (e.g. `user_signup`). |
| `referrerId` | string | The `clientProvidedId` of the referrer. Use this to credit the reward. |
| `refereeId` | string | The `clientProvidedId` of the new user who was referred. |
| `rewardClaimedAt` | string | ISO 8601 UTC timestamp of when the reward was granted. |
| `trackingId` | string | UUID of the referral tracking record. Use as an **idempotency key**. |
| `rewardStatus` | string | Always `"Completed"` when the webhook fires. |
| `projectId` | string | The project's `uniqueId`. Useful when sharing one endpoint across projects. |
| `projectName` | string | Human-readable project name. |
| `campaignId` | string | The campaign's `uniqueId`. |
## Handling Webhooks
Your endpoint must return a `2xx` HTTP response promptly (within a few seconds). Verify the secret, acknowledge immediately, then process asynchronously.
```ts
router.post('/webhooks/growth-rail', express.json(), async (req, res) => {
// 1. Verify the webhook secret
const authHeader = req.headers.authorization;
if (authHeader !== `Bearer ${process.env.GR_WEBHOOK_SECRET}`) {
return res.sendStatus(401);
}
// 2. Always acknowledge receipt immediately
res.sendStatus(200);
// 3. Process the reward asynchronously using trackingId as idempotency key
const { referrerId, trackingId, rewardStatus } = req.body;
if (rewardStatus === 'Completed') {
// Your business logic here — credit the referrer, send a notification, etc.
}
});
```
## Verifying Webhook Authenticity
Growth Rail sends your webhook secret as a **Bearer token** in the `Authorization` header. Always compare it with the secret configured for that webhook before processing the payload.
```ts
function verifyWebhook(req, res, next) {
const expected = 'Bearer ' + process.env.GR_WEBHOOK_SECRET;
const received = req.headers.authorization;
if (received !== expected) {
console.warn('Invalid webhook authentication');
return res.sendStatus(401);
}
next();
}
app.post('/webhooks/growth-rail', verifyWebhook, express.json(), handler);
```
> **Danger:** **Never skip verification.** Reject requests whose Bearer token does not match your configured webhook secret, otherwise anyone who discovers your endpoint URL could send fake reward payloads and credit users fraudulently.
## Idempotency
Webhooks may be delivered more than once due to network retries or redelivery from the dashboard. Always use the `trackingId` field as an idempotency key in your database before processing any reward — check whether the `trackingId` has already been handled and skip if so.
## Retry Behavior
Growth Rail retries transient failures. Authentication and other permanent client errors are dead-lettered immediately so a broken secret does not repeatedly hit your endpoint.
| Property | Value |
| --- | --- |
| Max attempts | **5** (initial + 4 retries) |
| Retry interval | Exponential backoff with jitter, starting at about **5 seconds** |
| Timeout | **10 seconds** total — respond quickly |
> **Warning:** **Respond quickly.** Always return `200` immediately and process the reward asynchronously. Long-running webhook handlers will cause timeouts and unnecessary retries.
## Monitoring & Logs
The dashboard shows delivery logs for each webhook with full details:
| Log Field | Description |
| --- | --- |
| **Timestamp** | When the delivery attempt was made |
| **Event type** | The trigger event name |
| **HTTP status** | Status code returned by your endpoint |
| **Response time** | Round-trip time in milliseconds |
| **Success** | Whether the delivery was successful |
## Managing Webhooks
All webhook management is available from the Growth Rail dashboard under **Integrations → Reward Webhooks**.
| Action | How |
| --- | --- |
| **Disable** | Toggle the webhook off to pause delivery without deleting it. Re-enable anytime. |
| **Retry a failed delivery** | Click the retry button on any failed log entry in the delivery logs. |
| **Delete** | Permanently removes the webhook and all its delivery logs. |
| **Edit** | Update the endpoint URL, name, or secret at any time. |
## Best Practices
- **Always verify** the Bearer token against your webhook secret
- **Respond with 200 immediately**, then process asynchronously
- **Use `trackingId` for idempotency** — webhooks may be delivered more than once
- **Use HTTPS endpoints** in production for encrypted delivery
- **Monitor delivery logs** in the dashboard to catch failures early
- **Don't do heavy processing** in the webhook handler — use a job queue for slow tasks
---
# Campaign Setup
A Campaign controls the visual and UX experience of your referral program — the floating invite button, the sharing modal, the new-user welcome banner, and the branding colors. Configure it once and the SDK renders everything automatically.
## Overview
Each project has a single Campaign configuration with two halves:
### Referrer Experience
What your **existing users** see. Includes the floating trigger button and the referral sharing modal/drawer with their referral link, copy button, and social sharing options.
### New User Experience
What **referred visitors** see when they arrive via a referral link. A promotional banner with custom text and positioning to encourage signup.
Configure these in the dashboard under **Campaign Setup**.
## Referrer Experience
The Referrer Experience is what your existing users see when they open your app. It consists of a **trigger button** and a **sharing modal or drawer**.
### Trigger Button
| Field | Values | Description |
| --- | --- | --- |
| `buttonType` | floating / edge / none | **floating** — a pill/FAB that hovers over content. **edge** — a tab pinned to the viewport edge. **none** — no button rendered (use manual trigger). |
| `buttonPosition` | `bottom-right` \| `bottom-left` \| `top-right` \| `top-left` | Corner of the viewport where the button appears. |
### Modal / Drawer
| Field | Type | Description |
| --- | --- | --- |
| `componentType` | `"modal"` \| `"drawer"` | **modal** — centered overlay with backdrop. **drawer** — side panel that slides in. |
| `modalTitle` | string (max 200 chars) | Headline of the sharing UI. |
| `modalDescription` | string (max 500 chars) | Body text explaining the benefit. |
#### Social sharing channels
The SDK includes built-in sharing buttons for Copy to clipboard, Twitter/X, Facebook, LinkedIn, WhatsApp, and Email.
### Branding / Theme
| Field | Format | Description |
| --- | --- | --- |
| `themeColor` | hex (`#RRGGBB`) | Primary brand color for button and accents. |
| `backgroundColor` | hex (`#RRGGBB`) | Modal/drawer background color. |
| `tintColor` | hex (`#RRGGBB`) | Tint overlay color for the backdrop. |
| `tintAlpha` | number (0–1) | Opacity of the tint overlay (0 = transparent, 1 = opaque). |
## Controlling the UI via SDK
The SDK fetches the campaign configuration automatically when `initAppUser()` is called and renders the trigger button and modal based on your dashboard settings. You can also control it programmatically:
```tsx
const { showReferralDashboard, showFloatingButton, hideFloatingButton } = useGrowthRail();
```
```typescript
GrowthRail.createTriggerButton({ position: 'bottom-right', displayMode: 'floating' });
GrowthRail.showReferralDashboard();
GrowthRail.destroyTriggerButton();
```
## New User Experience
When a referee (new user) lands on your site via a referral link, the SDK can automatically display a welcome banner to encourage signup.
| Field | Type | Description |
| --- | --- | --- |
| `promoText` | string (max 500 chars) | The promotional message shown to referred visitors. |
| `promoPosition` | string | One of: `top-left`, `top-center`, `top-right`, `bottom-left`, `bottom-center`, `bottom-right`. |
> **Note:** The banner only appears when the SDK detects a `referralCode` in the URL. It is dismissible by the user. No extra code needed — the SDK handles rendering and positioning.
## Webhooks
When a reward is granted for a referral under this campaign, all enabled webhooks fire automatically. Configure them in the Growth Rail dashboard under **Integrations → Reward Webhooks**.
For webhook payload details, authentication, retry behavior, and event types, see the [Webhooks guide](/guides/webhooks).
---
# JavaScript SDK
The `@growth-rail/core` package is a pure TypeScript, framework-agnostic SDK that works in any JavaScript environment — vanilla JS, Node.js, or as the foundation for the React and React Native SDKs.
## Installation
```bash
npm install @growth-rail/core
# or
yarn add @growth-rail/core
# or
pnpm add @growth-rail/core
```
> **Note:** **Version:** Current stable release is `@growth-rail/core@2.2.1`. Requires Node.js 18+.
## Initialization
Initialize the SDK once at app startup. All subsequent calls use the singleton instance.
```ts
// app.ts
import { GrowthRail } from '@growth-rail/core';
GrowthRail.init({
projectSecretKey: process.env.GROWTH_RAIL_SECRET, // required
cookieDomain: '.yourapp.com', // optional: cross-subdomain tracking
autoPageTrack: true, // optional: auto-detect referral codes in URL
debug: true, // optional: verbose console logging
});
```
### Configuration Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | — | **Required** Your project secret (`sk_...`). |
| `cookieDomain` | string? | current domain | Domain for tracking cookies. Use `.yourapp.com` for cross-subdomain attribution. |
| `autoPageTrack` | boolean? | `false` | Auto-detect `referralCode` in URL and track referral on page load. |
| `debug` | boolean? | `false` | Enable verbose console logging. Disable in production. |
## User Management
### `initAppUser(clientProvidedId)`
Registers a user with Growth Rail using your internal user ID. Creates the user if they don't exist, generates a unique referral code, and returns the user object with their referral link.
```ts
const user = await GrowthRail.initAppUser('user_123');
console.log(user.referralCode); // "ABC123"
console.log(user.referralLink); // "https://yourapp.com/signup?referralCode=ABC123"
```
| Return Field | Type | Description |
| --- | --- | --- |
| `id` | string | Growth Rail's internal user ID (GUID) |
| `uniqueId` | string | Unique identifier (e.g. `appuser_k8xn2m`) |
| `referralCode` | string | 6-character alphanumeric code |
| `referralLink` | string | Full shareable URL with referral code |
| `referrerExperience` | object | Campaign UI config (trigger button, modal, theme) |
### Other User Methods
| Method | Returns | Description |
| --- | --- | --- |
| `isUserReady()` | `boolean` | Whether `initAppUser()` has completed. |
| `ensureUserReady()` | `Promise` | Waits until the user is initialized, then returns the user object. |
| `getUserId()` | `string | undefined` | Returns the current user's Growth Rail ID, or undefined if not initialized. |
| `getReferralCode()` | `string | undefined` | Returns the current user's referral code. |
| `getReferralLink()` | `string` | Returns the fully constructed referral link for the current user. |
## Referral Tracking
### `trackReferral(referralCode, rewardEventName?)`
Tracks a referral when a new user arrives via a referral link. This is called **automatically** by the SDK when `autoPageTrack` is enabled and a `referralCode` is found in the URL. You only need to call this manually if you disable auto-tracking.
```ts
// Usually automatic — only call manually if needed
const result = await GrowthRail.trackReferral('ABC123', 'user_signup');
console.log(result.referralTrackingId); // UUID stored in cookie
console.log(result.promotionalText); // e.g. "Get 20% off!"
```
### Server-Side REST API (`POST /api/v1/sdk/track-reward-event`)
Reward tracking is performed server-side (or automatically via native Stripe & RevenueCat integrations). Send a `POST` request with your project secret key:
```ts
// Call POST /api/v1/sdk/track-reward-event from backend server
const response = await fetch('https://api.growthrail.dev/api/v1/sdk/track-reward-event', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-project-secret-key': 'sk_live_xxx'
},
body: JSON.stringify({
newUserId: 'user_123',
referralTrackingId: 'tr_789',
eventName: 'first_purchase',
environment: 'Production'
})
});
```
## Built-in UI
The core SDK includes rendered UI components that are injected into the DOM using Shadow DOM for style isolation. These work in any browser environment without framework dependencies.
### Referral Dashboard (Modal / Drawer)
```ts
// Open with default campaign settings
await GrowthRail.showReferralDashboard();
// Override options at runtime
await GrowthRail.showReferralDashboard({
title: 'Share & Earn!',
description: 'Invite friends to get 500 credits each.',
type: 'drawer', // 'modal' or 'drawer'
theme: {
primaryColor: '#6366f1',
tintColor: '#000000',
backgroundColor: '#ffffff',
},
});
```
The dashboard includes copy-to-clipboard, and social sharing buttons for Twitter, Facebook, LinkedIn, WhatsApp, and Email.
### Floating Trigger Button
```ts
// Show the floating invite button
GrowthRail.createTriggerButton({
position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
displayMode: 'floating', // 'floating' | 'edge'
});
// Remove the button
GrowthRail.destroyTriggerButton();
```
| Option | Values | Description |
| --- | --- | --- |
| `position` | `bottom-right` \| `bottom-left` \| `top-right` \| `top-left` | Which corner of the viewport |
| `displayMode` | `floating` \| `edge` | **floating**: pill/FAB hovering over content. **edge**: tab pinned to viewport edge. |
## Cookie Storage
The SDK uses cookies (30-day expiry) for referral attribution:
| Cookie | Purpose | Lifetime |
| --- | --- | --- |
| `gr_ref_code` | Stores the referral code detected from the URL | 30 days |
| `gr_tracked_referral` | Stores the tracking ID after a referral is recorded | 30 days |
Set the `cookieDomain` option during initialization for cross-subdomain attribution (e.g. `.yourapp.com` allows tracking across `www.yourapp.com` and `app.yourapp.com`).
## Utility Methods
| Method | Returns | Description |
| --- | --- | --- |
| `GrowthRail.isInitialized()` | `boolean` | Whether `init()` has been called. |
| `GrowthRail.isUserReady()` | `boolean` | Whether `initAppUser()` has completed. |
| `GrowthRail.ensureUserReady()` | `Promise` | Awaits user initialization, then returns the user. |
## API Endpoints
Under the hood, the SDK communicates with the Growth Rail API at `https://api.growthrail.dev`. All requests include the `X-GrowthRail-ProjectSecret` header automatically.
| SDK Method | API Endpoint |
| --- | --- |
| `initAppUser()` | `POST /api/v1/sdk/init-app-user` |
| `trackReferral()` | `POST /api/v1/sdk/track-referral` |
| `/api/v1/sdk/track-reward-event` | `POST /api/v1/sdk/track-reward-event` (Backend API / Webhook) |
| `showReferralDashboard()` | `POST /api/v1/sdk/new-user-experience` (fetches config) |
## TypeScript Types
All types are exported from the package for full type safety:
```ts
import type {
GrowthRailOptions,
AppUserType,
AppUserTypeWithId,
ReferralLink,
TrackReferralResponse,
ReferrerExperience,
ReferrerTriggerButton,
ReferrerModalOptions,
NewUserExperience,
TriggerButtonPosition,
EventPayload,
} from '@growth-rail/core';
```
## Complete Example
```ts
// app.ts
import { GrowthRail } from '@growth-rail/core';
// 1. Initialize the SDK
GrowthRail.init({
projectSecretKey: process.env.GROWTH_RAIL_SECRET!,
autoPageTrack: true,
debug: process.env.NODE_ENV !== 'production',
});
// 2. After user logs in, register them
async function onLogin(userId: string) {
const user = await GrowthRail.initAppUser(userId);
console.log('Referral link:', user.referralLink);
// 3. Show the floating invite button
GrowthRail.createTriggerButton({
position: 'bottom-right',
displayMode: 'floating',
});
}
// 4. Track rewards (Server-side API / Webhook)
// Call POST /api/v1/sdk/track-reward-event from your server, or let Stripe/RevenueCat handle attribution automatically.
// 5. On logout
function onLogout() {
GrowthRail.destroyTriggerButton();
}
```
## Debugging
Enable `debug: true` during initialization to see detailed console logs for every SDK operation:
- Initialization status and project validation
- User creation and referral code generation
- Referral tracking events and cookie storage
- API request/response payloads
- UI component rendering events
### Common issues
| Issue | Solution |
| --- | --- |
| `401 Unauthorized` | Check your Project Secret and Allowed Origins in the dashboard. |
| Referral code not detected | Ensure `autoPageTrack: true` or call `trackReferral()` manually. |
| UI not rendering | Call `initAppUser()` first — UI needs user context to render. |
| Cookie not persisting | Check `cookieDomain` setting. Cross-domain cookies require proper configuration. |
---
# React SDK
The `@growth-rail/react` package provides React hooks and context providers for integrating Growth Rail into React applications. Built on top of `@growth-rail/core`.
## Installation
```bash
npm install @growth-rail/react @growth-rail/core
# or
yarn add @growth-rail/react @growth-rail/core
# or
pnpm add @growth-rail/react @growth-rail/core
```
> **Note:** **Version:** Current stable release is `@growth-rail/react@2.1.1`. Requires React 18+ and `@growth-rail/core` as a peer dependency.
## GrowthRailProvider
Wrap your application root with `GrowthRailProvider`. This initializes the SDK and makes all hooks available throughout your component tree.
```tsx
import { GrowthRailProvider } from '@growth-rail/react';
function App() {
const { user } = useAuth(); // your auth hook
return (
);
}
```
### Provider Props
| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | Yes | Your project secret (`sk_...`). |
| `userId` | string? | No | If provided, automatically calls `initAppUser(userId)` on mount. |
| `debug` | boolean? | No | Enable verbose console logging. |
| `cookieDomain` | string? | No | Domain for tracking cookies. |
| `autoPageTrack` | boolean? | No | Auto-detect referral codes in the URL. |
> **Warning:** **When to pass `userId`:** If your user is already authenticated when the provider mounts, pass their ID immediately. If they log in later, use the `initAppUser()` method from the hook instead.
## useGrowthRail Hook
The primary hook for interacting with Growth Rail. Available in any component wrapped by `GrowthRailProvider`.
```tsx
import { useGrowthRail } from '@growth-rail/react';
function MyComponent() {
const {
initAppUser,
showReferralDashboard,
showFloatingButton,
hideFloatingButton,
isLoading,
error,
} = useGrowthRail();
// ...
}
```
### Hook Return Values
| Property | Type | Description |
| --- | --- | --- |
| `initAppUser(userId)` | `(id: string) => Promise` | Register a user and get their referral code/link. |
| `showReferralDashboard(options?)` | `(opts?) => void` | Open the referral sharing modal/drawer. |
| `showFloatingButton(options)` | `(opts) => void` | Show the floating trigger button. |
| `hideFloatingButton()` | `() => void` | Remove the floating trigger button. |
| `isLoading` | `boolean` | Whether an async operation is in progress. |
| `error` | `Error | null` | Last error that occurred, or null. |
## Usage Examples
### Initialize user after login
```tsx
import { useGrowthRail } from '@growth-rail/react';
export function LoginPage() {
const { initAppUser } = useGrowthRail();
const handleLogin = async (credentials) => {
const authUser = await loginService.login(credentials);
// Register with Growth Rail after successful login
const grUser = await initAppUser(authUser.id);
console.log('Referral link:', grUser.referralLink);
};
return ;
}
```
### Track a reward event (Server-Side API / Stripe / RevenueCat)
Reward tracking is handled server-side via `POST /api/v1/sdk/track-reward-event` (or automatically synced via Stripe / RevenueCat integrations).
```bash
POST https://api.growthrail.dev/api/v1/sdk/track-reward-event
Header: x-project-secret-key: sk_live_xxx
{
"newUserId": "user_123",
"referralTrackingId": "tr_789",
"eventName": "user_signup"
}
```
### Show referral sharing UI
```tsx
import { useGrowthRail } from '@growth-rail/react';
export function InvitePage() {
const { showReferralDashboard, showFloatingButton } = useGrowthRail();
return (
Invite Friends
Share your referral link and earn rewards!
);
}
```
### Override modal options
```tsx
showReferralDashboard({
title: 'Share & Earn!',
description: 'Invite friends to get 500 credits each.',
type: 'drawer',
theme: {
primaryColor: '#6366f1',
tintColor: '#000000',
backgroundColor: '#ffffff',
},
});
```
## ReferralDashboard Component
For embedding the referral dashboard directly in your page layout (instead of a modal/drawer overlay), use the `ReferralDashboard` component:
```tsx
import { ReferralDashboard } from '@growth-rail/react';
export function ReferralsPage() {
return (
Your Referral Program
toast.success('Referral link copied!')}
/>
);
}
```
| Prop | Type | Description |
| --- | --- | --- |
| `title` | string? | Custom title override for the dashboard. |
| `appearance` | ReferrerModalOptions? | Theme options (primaryColor, backgroundColor, etc.). |
| `onLinkCopied` | `() => void` | Callback fired when the user copies their referral link. |
## Complete Example
```tsx
import { GrowthRailProvider, useGrowthRail } from '@growth-rail/react';
// 1. Wrap your app
function App() {
return (
} />
} />
} />
);
}
// 2. Register users after login
function LoginPage() {
const { initAppUser } = useGrowthRail();
const onLogin = async (userId: string) => {
await initAppUser(userId);
navigate('/dashboard');
};
return ;
}
// 3. Show referral UI on dashboard
function Dashboard() {
const { showReferralDashboard } = useGrowthRail();
return (
Dashboard
);
}
// 4. Track rewards (Server-side API / Webhook)
// Call POST /api/v1/sdk/track-reward-event from your server, or let Stripe/RevenueCat handle attribution automatically.
```
## Error Handling
The hook exposes an `error` state for catching issues:
```tsx
const { error, isLoading, initAppUser } = useGrowthRail();
useEffect(() => {
if (error) {
console.error('Growth Rail error:', error.message);
// Show toast, log to error tracker, etc.
}
}, [error]);
```
### Common issues
| Issue | Solution |
| --- | --- |
| "useGrowthRail must be used within GrowthRailProvider" | Ensure `GrowthRailProvider` wraps the component tree above your hook usage. |
| `isLoading` stays true indefinitely | Check for network errors in the console. Verify your Project Secret and Allowed Origins. |
| User not auto-initialized | Ensure you're passing `userId` to the provider, or call `initAppUser()` manually. |
---
# React Native SDK
The `@growth-rail/react-native` package provides native-optimized hooks and components for React Native applications, with support for deep linking, install referrer tracking, and native UI components.
## Installation
```bash
npm install @growth-rail/react-native @growth-rail/core
# or
yarn add @growth-rail/react-native @growth-rail/core
```
> **Note:** **Version:** Current stable release is `@growth-rail/react-native@2.0.6`. Requires React Native 0.72+ and `@growth-rail/core` as a peer dependency.
## GrowthRailProvider
Wrap your app root with `GrowthRailProvider`. This initializes the SDK and provides context to all child components.
```tsx
// App.tsx
import { GrowthRailProvider } from '@growth-rail/react-native';
export default function App() {
return (
);
}
```
| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | Yes | Your project secret (`sk_...`). |
| `userId` | string? | No | If provided, automatically calls `initAppUser(userId)`. |
| `debug` | boolean? | No | Enable verbose console logging. |
## useGrowthRail Hook
The React Native hook provides all the same methods as the React web hook, plus additional mobile-specific methods and state.
```tsx
import { useGrowthRail } from '@growth-rail/react-native';
function MyScreen() {
const {
// Methods
initAppUser,
showReferralDashboard,
hideReferralDashboard,
showFloatingButton,
hideFloatingButton,
getReferralTrackingId,
getReferralLink,
// State
isInitialized,
isUserReady,
isLoading,
isDebugEnabled,
error,
} = useGrowthRail();
// ...
}
```
### Methods
| Method | Type | Description |
| --- | --- | --- |
| `initAppUser(userId)` | `Promise` | Register a user and get their referral code/link. |
| `showReferralDashboard(options?)` | `void` | Show the referral sharing UI. |
| `hideReferralDashboard()` | `void` | Dismiss the referral sharing UI. |
| `showFloatingButton(options)` | `void` | Show the floating trigger button. |
| `hideFloatingButton()` | `void` | Remove the floating trigger button. |
| `getReferralTrackingId()` | `Promise` | Get the stored referral tracking ID (from async storage). |
| `getReferralLink()` | `Promise` | Get the current user's referral link. |
### State Properties
| Property | Type | Description |
| --- | --- | --- |
| `isInitialized` | boolean | Whether the SDK has completed initialization. |
| `isUserReady` | boolean | Whether `initAppUser()` has completed. |
| `isLoading` | boolean | Whether an async operation is in progress. |
| `isDebugEnabled` | boolean | Whether debug mode is active. |
| `error` | Error \| null | Last error that occurred. |
## Native UI Components
The React Native SDK includes pre-built UI components optimized for mobile:
### ReferralDashboard
A full-screen or inline referral sharing component with copy-to-clipboard and social sharing.
```tsx
import { ReferralDashboard } from '@growth-rail/react-native';
function ReferralsScreen() {
return (
Alert.alert('Link copied!')}
/>
);
}
```
### TriggerButton
A floating action button that opens the referral dashboard when tapped.
```tsx
import { TriggerButton } from '@growth-rail/react-native';
function HomeScreen() {
return (
{/* Your screen content */}
);
}
```
### Banner
A promotional banner shown to users who arrive via a referral link.
```tsx
import { Banner } from '@growth-rail/react-native';
function SignupScreen() {
return (
{/* Your signup form */}
);
}
```
## Deep Linking
The React Native SDK supports **deferred deep linking** for attributing installs to referrals. When a user clicks a referral link but doesn't have the app installed:
1. The user is redirected to the App Store / Play Store
2. After installing, the user opens the app
3. The SDK uses **probabilistic device fingerprint matching** to attribute the install
4. The referral is tracked automatically
### URL Parameter Detection
For direct deep links (user already has the app), the SDK detects referral parameters from the incoming URL:
```tsx
// The SDK automatically handles deep link URLs like:
// yourapp://open?referralCode=ABC123&rewardEventName=user_signup
// For manual handling with React Navigation:
import { useGrowthRail } from '@growth-rail/react-native';
function DeepLinkHandler({ url }) {
const { trackReferral } = useGrowthRail();
useEffect(() => {
if (url) {
const params = new URLSearchParams(url.split('?')[1]);
const code = params.get('referralCode');
if (code) {
trackReferral(code, params.get('rewardEventName'));
}
}
}, [url]);
}
```
> **Note:** **Install referrer tracking:** The SDK automatically uses the Android Install Referrer API and iOS clipboard-based attribution when available. No extra configuration needed.
## Storage
Unlike the web SDK which uses cookies, the React Native SDK uses **AsyncStorage** (or your configured storage adapter) to persist referral tracking data across app sessions.
| Key | Purpose |
| --- | --- |
| `gr_ref_code` | Referral code from the deep link |
| `gr_tracked_referral` | Tracking ID after referral is recorded |
## Theme Support
The React Native SDK automatically detects the device's light/dark mode preference and adjusts the built-in UI components accordingly. You can also override the theme via the campaign configuration in the dashboard.
## Complete Example
```tsx
// App.tsx
import React, { useEffect } from 'react';
import { View, Button, Alert } from 'react-native';
import { GrowthRailProvider, useGrowthRail } from '@growth-rail/react-native';
// 1. Wrap your app
export default function App() {
return (
);
}
// 2. Use the hook in your screens
function MainScreen() {
const {
initAppUser,
showReferralDashboard,
isUserReady,
isLoading,
} = useGrowthRail();
// Register user after auth
useEffect(() => {
initAppUser('user_123');
}, []);
// Show referral UI
const handleInvite = () => {
showReferralDashboard({
title: 'Invite Friends',
description: 'Share your link and earn 500 credits!',
});
};
return (
);
}
```
## Differences from Web SDK
| Feature | React (Web) | React Native |
| --- | --- | --- |
| Storage | Cookies (30-day expiry) | AsyncStorage (persistent) |
| Deep linking | URL query parameter detection | Universal links, install referrer, fingerprint matching |
| UI rendering | Shadow DOM injection | Native React Native components |
| Theme | CSS-based theming | Auto light/dark mode detection |
| `hideReferralDashboard()` | Not available (modal auto-closes) | Available for programmatic dismissal |
| `getReferralTrackingId()` | Not exposed (cookie-based) | Async method to read from storage |
## Troubleshooting
### Common issues
| Issue | Solution |
| --- | --- |
| Deep link not attributed | Check that your mobile app details (App Store URL, Play Store URL) are configured in Project Settings. |
| Fingerprint matching fails | Matching uses a 2-hour window. Ensure the install happens within 2 hours of the click. |
| UI not rendering | Ensure `initAppUser()` completes before showing UI. Check `isUserReady` state. |
| AsyncStorage errors | Ensure `@react-native-async-storage/async-storage` is installed and linked. |
---
# iOS SDK
The GrowthRail iOS SDK is the native Swift counterpart to the React Native SDK. It provides attribution and referral APIs, automatic referral UI (trigger, banner, and dashboard), and deep-link handling for SwiftUI and UIKit apps.
## Installation
The SDK is distributed as a binary Swift package from [GrowthRail/growthrail-ios-sdk](https://github.com/GrowthRail/growthrail-ios-sdk). There is no CocoaPods artifact.
### Install with Xcode
1. Choose **File → Add Package Dependencies**.
2. Enter `https://github.com/GrowthRail/growthrail-ios-sdk.git`.
3. Select a released version (prefer an exact version for production).
4. Add the `GrowthRail` product for full UI, or `GrowthRailCore` for a headless integration.
### Install from Package.swift
```swift
dependencies: [
.package(
name: "GrowthRail",
url: "https://github.com/GrowthRail/growthrail-ios-sdk.git",
from: "0.1.8"
),
],
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "GrowthRail", package: "GrowthRail"),
// Or: .product(name: "GrowthRailCore", package: "GrowthRail"),
]
),
]
```
> **Note:** Requirements: Xcode 15+, Swift 5.9+, iOS 15+. Current public binary release is `0.1.8`. SwiftPM verifies XCFramework checksums automatically.
| Product | Use when |
| --- | --- |
| `GrowthRail` | Full SDK including SwiftUI/UIKit host UI. Re-exports Core APIs via `import GrowthRail`. |
| `GrowthRailCore` | Attribution and referral APIs only — you render your own UI and observe `GrowthRail.shared.state`. |
## Configuration
Call `GrowthRail.configure` once at app startup (for example in `App.init()` or `application(_:didFinishLaunchingWithOptions:)`). Use a publishable mobile key; never ship a server secret in the app binary.
```swift
import GrowthRail
import SwiftUI
@main
struct ExampleApp: App {
init() {
GrowthRail.configure(
GrowthRailConfiguration(
projectSecretKey: "sk_your_publishable_mobile_key",
appearance: .dark,
theme: GrowthRailTheme(primaryColor: "#2563eb"),
debug: true
)
)
}
var body: some Scene {
WindowGroup {
ContentView()
.growthRailHost()
.onOpenURL { _ = GrowthRail.shared.handle(url: $0) }
}
}
}
```
### Configuration Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | — | **Required.** Publishable mobile key (`sk_...`). |
| `apiURL` | URL | production API | Override for staging or local API testing. |
| `appearance` | `.light` \| `.dark`? | system | Preferred appearance for automatic UI. |
| `theme.primaryColor` | string? | campaign / `#2563eb` | Hex primary color for trigger and dashboard chrome. |
| `disableTriggerButton` | bool | `false` | Never show the automatic floating/edge trigger. |
| `userId` | string? | nil | If set, identifies the user automatically after bootstrap. |
| `debug` | bool | `false` | Verbose logging. Disable in production. |
> **Note:** `cookieDomain` and `autoPageTrack` exist for React Native source compatibility and have no effect on iOS.
## Automatic UI
With the `GrowthRail` product, attach the host once at the root. The host renders the server-configured trigger, new-user banner, and referral dashboard.
### SwiftUI
```swift
ContentView()
.growthRailHost()
// Or: .growthRailHost(sdk: GrowthRail.shared)
```
### UIKit
```swift
final class HomeViewController: UIViewController {
private let growthRailHost = GrowthRailUIKitHost()
override func viewDidLoad() {
super.viewDidLoad()
growthRailHost.attach(to: self)
}
deinit {
growthRailHost.detach()
}
}
```
Backend campaign settings drive layout at runtime:
| Field | Values | Behavior |
| --- | --- | --- |
| `trigger.displayMode` | `floating` \| `edge` \| `none` | Circle inset trigger, flush edge tab, or hidden. Also respect `disableTriggerButton`. |
| `trigger.position` | `bottom-right`, `bottom-left`, `top-right`, `top-left` | Corner placement for the trigger. |
| `modal.componentType` | `modal` \| `drawer` | Centered card over a dimmed backdrop, or system bottom sheet with detents. |
| `banner.position` | `center-top`, `center-bottom`, `left-*`, `right-*` | New-user promotional banner placement after attributed install/open. |
## User Management
Call `initAppUser` after authentication with a stable app user ID (for example a RevenueCat app user ID or your database ID — never an email). Any attribution captured from an incoming link is bound automatically.
```swift
Task {
do {
let user = try await GrowthRail.shared.initAppUser(userID)
let referralLink = await GrowthRail.shared.getReferralLink()
let attributionToken = await GrowthRail.shared.getAttributionToken()
} catch {
// Present or log the integration error
}
}
```
`GrowthRail.shared.state` is an `ObservableObject` with `isInitialized`, `isUserReady`, `currentUser`, `isLoading`, `error`, referral fields, trigger visibility, banner state, and dashboard state.
## Common APIs
| Need | API |
| --- | --- |
| Identify user | `try await GrowthRail.shared.initAppUser(userID)` or `identify(userID)` |
| Open / close dashboard | `showReferralDashboard(options:)`, `hideReferralDashboard()` |
| Incoming attribution | `getReferralTrackingId()`, `getAttributionToken()`, `getCapturedReferralCode()` |
| Current referral data | `getReferralLink()`, `getReferralCode()` |
| Track a non-standard source | `try await trackReferral(code, rewardEventName:)` |
| Dismiss banner | `dismissBanner()` |
| Custom UI | Observe `GrowthRail.shared.state`; optionally embed `ReferralDashboard` / `TriggerButton` |
### Dashboard options override
Pass `ReferrerModalOptions` to override title, description, presentation, appearance, or primary color for a single open:
```swift
GrowthRail.shared.showReferralDashboard(
options: ReferrerModalOptions(
title: "Invite Friends",
description: "Share your link and earn rewards.",
componentType: .drawer,
appearance: .dark,
theme: GrowthRailTheme(primaryColor: "#2563eb")
)
)
```
## Deep Linking
Register a custom URL scheme or Universal Link, then forward URLs to GrowthRail. The handler returns `true` only when the URL contains a non-empty `referralCode` query parameter.
### SwiftUI
```swift
.onOpenURL { url in
_ = GrowthRail.shared.handle(url: url)
}
```
### UIKit / AppDelegate
```swift
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
GrowthRail.shared.handle(url: url)
}
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard let url = userActivity.webpageURL else { return false }
return GrowthRail.shared.handle(url: url)
}
```
Deferred attribution uses privacy-preserving fingerprint matching on first launch and does not collect IDFA. The SDK persists the referrer code and tracking ID, then forwards the tracking ID when the user is initialized.
> **Simulator tip:** `xcrun simctl openurl booted 'your-app://open?referralCode=TEST123'`
## Complete Example
```swift
import GrowthRail
import SwiftUI
@main
struct ExampleApp: App {
init() {
GrowthRail.configure(
GrowthRailConfiguration(
projectSecretKey: "sk_your_publishable_mobile_key",
debug: true
)
)
}
var body: some Scene {
WindowGroup {
ContentView()
.growthRailHost()
.onOpenURL { _ = GrowthRail.shared.handle(url: $0) }
.task {
_ = try? await GrowthRail.shared.initAppUser("user_123")
}
}
}
}
struct ContentView: View {
var body: some View {
Button("Invite Friends") {
GrowthRail.shared.showReferralDashboard()
}
}
}
```
## Differences from React Native
| Feature | React Native | iOS (Swift) |
| --- | --- | --- |
| Distribution | npm (`@growth-rail/react-native`) | SwiftPM binary XCFrameworks |
| Entry point | `GrowthRailProvider` + hooks | `GrowthRail.configure` + `.growthRailHost()` |
| Storage | AsyncStorage | App Group / UserDefaults-backed storage |
| UI | React Native components | SwiftUI overlay / UIKit host |
| Modal vs drawer | Custom animated views | Centered card vs system bottom sheet |
## Troubleshooting
| Issue | Solution |
| --- | --- |
| Trigger never appears | Confirm `initAppUser` succeeded, `trigger.displayMode` is not `none`, and `disableTriggerButton` is false. Attach `.growthRailHost()` at the root. |
| `Symbol not found` / duplicate class warnings | Use binary package `0.1.4+`. Reset package caches and clean build so you are not mixing stale XCFrameworks. |
| Deep link ignored | URL must include a non-empty `referralCode` query parameter, and you must forward it with `handle(url:)`. |
| `Library not loaded: @rpath/GrowthRail.framework` | Depend on the public binary package or the local automatic/static `packages/ios` package — do not force dynamic products in a path-dependent host app. |
---
# Android SDK
The GrowthRail Android SDK is the native Kotlin counterpart to the React Native SDK. It provides attribution and referral APIs, automatic referral UI (trigger, banner, and dashboard), and deep-link handling for View-system apps.
## Installation
Published to Maven Central as two artifacts:
```kotlin
repositories {
google()
mavenCentral()
}
dependencies {
// Complete SDK: core APIs plus the native referral UI.
implementation("dev.growthrail:growthrail:0.1.1")
// Or, for a headless/custom UI integration:
// implementation("dev.growthrail:growthrail-core:0.1.1")
}
```
> **Note:** Requirements: minSdk 23, compileSdk 35, JDK 17. Current Maven Central release is `0.1.1`.
| Artifact | Use when |
| --- | --- |
| `dev.growthrail:growthrail` | Full SDK including the View overlay host. Depends on `growthrail-core` transitively. |
| `dev.growthrail:growthrail-core` | Attribution and referral APIs only — collect `GrowthRail.state` and render your own UI. |
## Configuration
Call `GrowthRail.initialize` once in your `Application` class. Use a publishable mobile key; never embed a server secret in the APK.
```kotlin
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
GrowthRail.initialize(
this,
GrowthRailConfiguration(
projectSecretKey = "sk_your_publishable_mobile_key",
appearance = GrowthRailAppearance.DARK,
theme = GrowthRailTheme(primaryColor = "#2563eb"),
debug = BuildConfig.DEBUG,
),
)
}
}
```
### Configuration Options
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `projectSecretKey` | string | — | **Required.** Publishable mobile key (`sk_...`). |
| `apiUrl` | string? | production API | Override for staging or local API testing. |
| `appearance` | `LIGHT` \| `DARK`? | system | Preferred appearance for automatic UI. |
| `theme.primaryColor` | string? | campaign / `#2563eb` | Hex primary color for trigger and dashboard chrome. |
| `disableTriggerButton` | boolean | `false` | Never show the automatic floating/edge trigger. |
| `userId` | string? | null | If set, identifies the user automatically after bootstrap. |
| `debug` | boolean | `false` | Verbose logging. Disable in production. |
> **Note:** `cookieDomain` and `autoPageTrack` exist for React Native source compatibility and have no effect on Android.
## Automatic UI
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GrowthRailOverlay.attach(this)
}
```
Backend campaign settings drive layout at runtime:
| Field | Values | Behavior |
| --- | --- | --- |
| `trigger.displayMode` | `floating` \| `edge` \| `none` | Circle inset trigger, flush edge tab, or hidden. |
| `trigger.position` | four corners | Corner placement for the trigger. |
| `modal.componentType` | `modal` \| `drawer` | Centered card, or bottom sheet with drag handle (max ~90% height). |
| `banner.position` | `center-top`, `center-bottom`, `left-*`, `right-*` | New-user banner placement. |
## User Management
```kotlin
lifecycleScope.launch {
val user = GrowthRail.initAppUser(userId)
val referralLink = GrowthRail.getReferralLink()
val attributionToken = GrowthRail.getAttributionToken()
}
```
`GrowthRail.state` is a `StateFlow` with initialization, user, referral, trigger, banner, and dashboard fields.
## Common APIs
| Need | API |
| --- | --- |
| Identify user | `suspend GrowthRail.initAppUser(userId)` or `identify(userId)` |
| Open / close dashboard | `showReferralDashboard(options)`, `hideReferralDashboard()` |
| Incoming attribution | `getReferralTrackingId()`, `getAttributionToken()`, `getCapturedReferralCode()` |
| Current referral data | `getReferralLink()`, `getReferralCode()` |
| Track a non-standard source | `suspend trackReferral(referralCode, rewardEventName)` |
| Dismiss banner | `dismissBanner()` |
| Custom UI | Collect `GrowthRail.state`; optionally embed `ReferralDashboard` / `TriggerButton` |
## Deep Linking
Forward intents that include a non-empty `referralCode` query parameter:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
intent.data?.let(GrowthRail::handleDeepLink)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let(GrowthRail::handleDeepLink)
}
```
On first launch the SDK checks Google Play Install Referrer, then uses the same privacy-preserving match-link fallback as React Native.
> **Emulator tip:** `adb shell am start -a android.intent.action.VIEW -d 'your-app://open?referralCode=TEST123'`
## Differences from React Native
| Feature | React Native | Android (Kotlin) |
| --- | --- | --- |
| Distribution | npm (`@growth-rail/react-native`) | Maven Central AARs |
| Entry point | `GrowthRailProvider` + hooks | `GrowthRail.initialize` + `GrowthRailOverlay.attach` |
| Storage | AsyncStorage | SharedPreferences-backed storage |
| UI | React Native components | View-system overlay host |
| Modal vs drawer | Custom animated views | Centered dialog vs bottom-gravity sheet |
## Troubleshooting
| Issue | Solution |
| --- | --- |
| Trigger never appears | Use the full `growthrail` artifact, attach `GrowthRailOverlay` after `setContentView`, then call `initAppUser`. |
| Deep link ignored | URL must include a non-empty `referralCode` and you must forward it with `handleDeepLink`. |
| Local UI cannot resolve core | Publish `growthrail-core` to Maven Local before `growthrail`. |
| Missing key errors | Supply a non-empty publishable mobile key or backend-issued token. |
---
# Clerk Integration
Use Clerk's signed `user.created` webhook as signup proof without writing a customer backend hook.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Clerk**, and create the matching Sandbox or Production connection.
2. Copy its generated endpoint into **Clerk Dashboard > Webhooks**.
3. Subscribe to `user.created` and save Clerk's `whsec_...` Svix signing secret in Growth Rail.
4. Initialize Growth Rail with the same stable Clerk ID sent as `data.id`:
```ts
await GrowthRail.initAppUser(clerkUser.id);
```
The SDK automatically sends its stored opaque referral tracking ID during initialization. Growth Rail completes a referral only after the verified Clerk event and that SDK claim join. It never joins users by email.
Dashboard/API-created Clerk users may also emit `user.created`; without a pending attribution claim they are recorded but not rewarded.
---
# Stytch Integration
Use Stytch's signed Consumer user or B2B member creation webhook as signup proof without adding a customer backend hook.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Stytch**, and create a Sandbox or Production connection.
2. Add its endpoint under **Stytch Dashboard > Webhooks**.
3. For Consumer projects select `direct.user.create`; for B2B select `direct.member.create`.
4. Paste the endpoint's `whsec_...` Svix signing secret into Growth Rail.
5. Initialize Growth Rail with `user.user_id` or `member.member_id`:
```ts
await GrowthRail.initAppUser(stytchUserId);
```
Dashboard and SCIM creation use separate event variants and are not treated as self-service signup proof. Growth Rail verifies the raw Svix signature and deduplicates by Stytch event ID.
---
# WorkOS AuthKit Integration
Use WorkOS AuthKit's signed `user.created` webhook as signup proof.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **WorkOS AuthKit**, and create a connection.
2. Create a WorkOS webhook endpoint using the generated URL and subscribe to `user.created`.
3. Paste the returned `whsec_...` endpoint secret into Growth Rail.
4. Pass the same WorkOS user ID delivered as `data.id`:
```ts
await GrowthRail.initAppUser(workosUser.id);
```
Growth Rail validates the `WorkOS-Signature` timestamp and HMAC over the exact raw body. Test every social and enterprise signup path enabled in your WorkOS staging environment.
---
# Logto Integration
Use Logto's signed `PostRegister` event after a user completes an Experience API signup.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Logto**, and create a connection.
2. Add its endpoint in **Logto Console > Webhooks** and subscribe to `PostRegister`.
3. Paste the Logto webhook signing key into Growth Rail.
4. Initialize Growth Rail with the same `userId` delivered by Logto:
```ts
await GrowthRail.initAppUser(logtoUser.id);
```
Growth Rail verifies `logto-signature-sha-256` against the exact raw body. `User.Created` is a broader data-mutation event and is intentionally not treated as completed signup proof.
---
# Kinde Integration
Verify Kinde's signed `user.created` webhook JWT against the tenant's public JWKS.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Kinde**, and create a connection.
2. Create a Kinde webhook for `user.created` using the generated endpoint.
3. Save `https://your-subdomain.kinde.com/.well-known/jwks` as the verification value in Growth Rail.
4. Pass the same user ID delivered as `data.user.id`:
```ts
await GrowthRail.initAppUser(kindeUser.id);
```
Kinde sends the webhook body as an RS256-signed JWT. Growth Rail only fetches HTTPS JWKS URLs under `kinde.com`, caches public keys briefly, and deduplicates using `webhook-id`. User imports do not emit `user.created`.
---
# FusionAuth Integration
Verify `user.registration.create.complete` after a FusionAuth application registration finishes.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **FusionAuth**, and create a connection.
2. In FusionAuth, create a webhook scoped to the intended tenant/application and select `user.registration.create.complete`.
3. Enable **Sign events** with a Key Master HMAC, RSA, or EC key.
4. Paste the HMAC secret, compact JWKS JSON, or PEM public key into Growth Rail. Never provide an RSA/EC private key.
5. Initialize Growth Rail with `event.user.id`:
```ts
await GrowthRail.initAppUser(fusionAuthUser.id);
```
Growth Rail validates the signature JWT and compares its `request_body_sha256` claim with the exact raw body. Tenant-wide user creation and transactional pre-completion events are not accepted as signup proof.
---
# Frontegg Integration
Use a signed Frontegg interactive signup or explicit user-created event as signup proof.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Frontegg**, and create a connection.
2. Create a Frontegg webhook connection using the generated endpoint.
3. Select `frontegg.user.signedUp`; add `frontegg.user.created` only when non-interactive creation is intentionally in scope.
4. Configure a webhook **Secret key** in Frontegg and save the same value in Growth Rail.
5. Initialize Growth Rail with the stable Frontegg user ID:
```ts
await GrowthRail.initAppUser(fronteggUser.id);
```
Growth Rail verifies the HS256 JWT sent in `x-webhook-secret`. SCIM creation is a separate event and is intentionally excluded.
---
# Okta Integration
Use an Okta Event Hook to process verified `user.lifecycle.create` events, including batched deliveries.
## Setup
1. In Growth Rail, open **Integrations > Auth**, choose **Okta**, and create a connection.
2. Generate an Authorization value and save its exact full value in Growth Rail.
3. Create an Okta Event Hook with the generated endpoint, configure the same Authorization header, and select `user.lifecycle.create`.
4. Click **Verify** in Okta. Growth Rail automatically answers the one-time `x-okta-verification-challenge` GET.
5. Initialize Growth Rail with the stable Okta User target ID:
```ts
await GrowthRail.initAppUser(oktaUser.id);
```
Okta may batch multiple `data.events`, deliver at least once, or deliver out of order. Growth Rail processes and deduplicates each event independently. Admin, import, and JIT creation only earns a reward when the same user has a pending SDK attribution claim.
---
# Stripe
Attribute referrals to your Stripe billing — no conversion code required.
## How It Works
Connect your Stripe account to Growth Rail by registering a webhook in your Stripe dashboard. Growth Rail listens to two Stripe events:
- **customer.created** → Recorded as a referral signup
- **invoice.paid** → Recorded as a paid conversion; fires the referrer's reward
When Stripe fires `customer.created`, Growth Rail reads the customer metadata you attached at creation time and records a referral signup against the referrer identified by `gr_ucc`. When Stripe fires `invoice.paid`, Growth Rail records a paid conversion, captures the invoice amount as revenue, and triggers the referrer's reward.
> **Note:** **Prerequisites:** You need an active Stripe account and the Growth Rail SDK installed in your app. The SDK exposes `getCapturedReferralCode()`, which you call client-side to read the incoming referral code from the `?referralCode=...` URL the referred user landed on.
## 1. Get your webhook endpoint URL
In the Growth Rail dashboard, open **Integrations** from the sidebar and select the **Stripe** tab. It shows your project's **Sandbox** and **Production** webhook endpoint URLs, each with a copy button.
> **Note:** These URLs are unique to your project and environment. Always copy them straight from the dashboard — there's nothing to construct or fill in by hand.
## 2. Create a Stripe webhook
In your [Stripe Dashboard](https://dashboard.stripe.com/webhooks), go to **Developers → Webhooks** and click **Add endpoint**. Paste the Growth Rail URL from Step 1 and enable exactly these two events:
| Event | What it triggers in Growth Rail |
| --- | --- |
| `customer.created` | Referral signup recorded |
| `invoice.paid` | Paid conversion recorded; referrer reward fires |
Repeat this for both sandbox and production Stripe accounts using their respective Growth Rail endpoint URLs.
## 3. Copy the Stripe signing secret into Growth Rail
After creating the webhook in Stripe, reveal the **Signing secret** (`whsec_...`) on the webhook detail page. Then, in the Growth Rail dashboard, open **Integrations** from the sidebar, select the **Stripe** tab, and paste it into the **Signing secret** field for the matching environment (sandbox or production).
> **Note:** Growth Rail uses this secret to verify that every incoming webhook request genuinely originates from Stripe. Without it, events are rejected.
## 4. Attach attribution metadata when creating Stripe customers
When your backend creates a Stripe customer for a referred user, include the Growth Rail attribution keys in the `metadata` object. Retrieve the referral code client-side using the Growth Rail SDK before your signup flow completes, then pass it to your server.
```ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// growthRailUcc comes from the client via getCapturedReferralCode()
async function createStripeCustomer(
email: string,
user: { id: string },
growthRailUcc: string,
org?: { id: string },
) {
const customer = await stripe.customers.create({
email,
metadata: {
gr_ucc: growthRailUcc, // referrer's referral code (required)
gr_user_id: user.id, // your user id (required)
// gr_org_id: org?.id, // optional, for B2B / org-level referrals
},
});
return customer;
}
```
On the client side, call `getCapturedReferralCode()` from the Growth Rail SDK and pass the result to your server when the user signs up:
```tsx
import { useGrowthRail } from '@growth-rail/react';
export function SignupForm() {
const { getCapturedReferralCode } = useGrowthRail();
async function handleSubmit(email: string) {
const referralCode = getCapturedReferralCode(); // may be null
await fetch('/api/signup', {
method: 'POST',
body: JSON.stringify({ email, referralCode }),
});
}
return (/* your form */);
}
```
```typescript
import { GrowthRail } from '@growth-rail/core';
async function handleSignup(email: string) {
const referralCode = GrowthRail.getCapturedReferralCode(); // may be null
await fetch('/api/signup', {
method: 'POST',
body: JSON.stringify({ email, referralCode }),
});
}
```
## Metadata Reference
### Stripe customer metadata fields
| Key | Required | Description |
| --- | --- | --- |
| `gr_ucc` | Yes | The referral code that identifies the referrer. Obtain it client-side by calling `getCapturedReferralCode()` from the Growth Rail SDK. This is the code the referred user arrived with in their `?referralCode=...` URL. |
| `gr_user_id` | Yes | Your own unique identifier for the referred user (e.g. your database user ID). Used by Growth Rail to associate the referral and conversion with the correct user record. |
| `gr_org_id` | No | Organization ID for B2B or org-level referral attribution. Set this when your product tracks referrals at the organization rather than the individual user level. |
> **Warning:** **Security & limitations:**
> - Growth Rail only needs the **Stripe signing secret** (`whsec_...`) — never your Stripe API key. Do not paste your Stripe secret key into Growth Rail or share it with any third party.
> - Reward **reversal on refunds or subscription cancellations** is not yet supported. Rewards that were already granted will not be automatically revoked when an invoice is refunded or a subscription is cancelled. This feature is coming soon.
---
# RevenueCat
Attribute mobile subscription revenue to referrals — no conversion code required.
## How It Works
Connect your RevenueCat account to Growth Rail by registering a webhook in your RevenueCat dashboard. When a referred user makes an in-app purchase, RevenueCat fires a single event that carries the full attribution:
- **User installs** — Attribution code stored as subscriber attribute
- **INITIAL_PURCHASE** — Attributed conversion recorded; referrer reward fires
Growth Rail listens for `INITIAL_PURCHASE` and `NON_RENEWING_PURCHASE` events from RevenueCat. When one arrives, Growth Rail reads the `gr_ucc` subscriber attribute you set at install time to identify the referrer, uses RevenueCat's `app_user_id` to identify the referee, records the purchase price as revenue, and triggers the referrer's reward — all from a single webhook event, with no conversion code required in your checkout flow.
> **Note:** **Prerequisites:** You need an active RevenueCat account with the RevenueCat SDK already integrated in your mobile app. You also need the Growth Rail React Native SDK installed so you can call `getCapturedReferralCode()` to retrieve the incoming referral code from the deep link.
### 1. Get your webhook endpoint URL
In the Growth Rail dashboard, open **Integrations** from the sidebar and select the **RevenueCat** tab. It shows your project's **Sandbox** and **Production** webhook endpoint URLs, each with a copy button.
> **Note:** These URLs are unique to your project and environment. Always copy them straight from the dashboard — there's nothing to construct or fill in by hand.
### 2. Add a webhook in RevenueCat
In your RevenueCat dashboard, go to **Integrations → Webhooks** and click **+ New webhook**. Paste the Growth Rail URL from Step 1 as the endpoint. Then set the **Authorization header** value to the shared secret shown in the Growth Rail dashboard (open **Integrations** from the sidebar and select the **RevenueCat** tab). Finally, enable exactly these two event types:
| Event | What it triggers in Growth Rail |
| --- | --- |
| `INITIAL_PURCHASE` | Attributed conversion recorded; referrer reward fires |
| `NON_RENEWING_PURCHASE` | Attributed conversion recorded; referrer reward fires |
Repeat this for both sandbox and production RevenueCat projects using their respective Growth Rail endpoint URLs.
### 3. Save the shared secret in Growth Rail
Return to the Growth Rail dashboard, open **Integrations** from the sidebar, select the **RevenueCat** tab, and paste the same secret into the **Authorization secret** field for the matching environment (sandbox or production).
> **Note:** Growth Rail verifies the `Authorization` header on every incoming RevenueCat request using this secret. Without it, events are rejected.
### 4. Set the attribution attribute in your mobile app
After the user logs in via `Purchases.logIn(userId)`, retrieve the captured referral code using the Growth Rail SDK and store it as the `gr_ucc` subscriber attribute. RevenueCat will include this attribute in every subsequent webhook event for that user.
**React Native**
```tsx
import Purchases from 'react-native-purchases';
import { useGrowthRail } from '@growth-rail/react-native';
const { getCapturedReferralCode } = useGrowthRail();
// Call this after Purchases.logIn(userId) resolves
const grUcc = await getCapturedReferralCode();
if (grUcc) {
await Purchases.setAttributes({ gr_ucc: grUcc });
}
```
**Swift**
```typescript
// After Purchases.shared.logIn(userId) completes
// Retrieve the referral code from your deep-link handling layer
if let code = GrowthRail.getCapturedReferralCode() {
Purchases.shared.attribution.setAttributes(["gr_ucc": code])
}
```
**Kotlin**
```typescript
// After Purchases.sharedInstance.logIn(userId) completes
// Retrieve the referral code from your deep-link handling layer
val grUcc = GrowthRail.getCapturedReferralCode()
if (grUcc != null) {
Purchases.sharedInstance.setAttributes(mapOf("gr_ucc" to grUcc))
}
```
The React Native `getCapturedReferralCode()` is **async** — always `await` it. The referee is identified automatically by RevenueCat's `app_user_id` (the value you pass to `Purchases.logIn`), so no additional user-identity attribute is needed.
## Subscriber Attributes Reference
| Key | Required | Description |
| --- | --- | --- |
| `gr_ucc` | Yes | The referral code that identifies the referrer. Obtain it by calling `getCapturedReferralCode()` from the Growth Rail SDK after the user arrives via a referral deep link. |
| `gr_org_id` | No | Organization ID for B2B or org-level referral attribution. Set this when your product tracks referrals at the organization rather than the individual user level. |
| *app_user_id* | Automatic | RevenueCat's built-in user identifier — the value you pass to `Purchases.logIn(userId)`. Growth Rail uses this to identify the referee automatically. No extra attribute required. |
Custom attribute key rules: alphanumeric characters plus `_` and `-`, maximum 40 characters, must not start with `$`. Both `gr_ucc` and `gr_org_id` satisfy these constraints.
> **Warning:** **Security & limitations:**
> - Growth Rail only needs the **Authorization header secret** you configure in RevenueCat — never your RevenueCat API key. Do not paste your RevenueCat API key into Growth Rail or share it with any third party.
> - Reward **reversal on refunds or subscription cancellations** (`CANCELLATION` / `EXPIRATION` events) is not yet supported. Rewards that were already granted will not be automatically revoked when a subscription lapses. This feature is coming soon.
---
# Lemon Squeezy Integration
Attribute a paid Lemon Squeezy order using `order_created`, `X-Signature`, and checkout custom data.
## Setup
1. In Growth Rail, open **Integrations > Purchases**, choose **Lemon Squeezy**, and create an environment-specific connection.
2. Add the generated URL in **Lemon Squeezy > Settings > Webhooks**, select `order_created`, and save the same signing secret in Growth Rail.
3. Put the SDK attribution token and stable user ID into checkout custom data:
```ts
const url = new URL(checkoutUrl);
const token = GrowthRail.getAttributionToken();
if (token) {
url.searchParams.set('checkout[custom][gr_attribution]', token);
url.searchParams.set('checkout[custom][gr_user_id]', currentUser.id);
}
```
Lemon Squeezy returns these fields under `meta.custom_data`. Growth Rail verifies the raw-body HMAC, deduplicates by order, and never uses buyer email as an attribution fallback.
---
# Paddle Integration
Attribute completed Paddle Billing transactions using signed notifications and `customData`.
## Setup
1. In Growth Rail, open **Integrations > Purchases**, choose **Paddle**, and create a Sandbox or Production connection.
2. Add its URL under **Paddle > Developer tools > Notifications**, subscribe to `transaction.completed`, and paste the destination secret into Growth Rail.
3. Add Growth Rail values when opening checkout:
```ts
Paddle.Checkout.open({
items: [{ priceId, quantity: 1 }],
customData: {
gr_attribution: GrowthRail.getAttributionToken(),
gr_user_id: currentUser.id,
},
});
```
Growth Rail waits for `transaction.completed` with `completed` status. A checkout redirect is not payment proof. Events are deduplicated by Paddle event and transaction IDs.
---
# Adapty Integration
Attribute `subscription_started`, `trial_converted`, and `non_subscription_purchase` events from Adapty.
## Setup
1. In Growth Rail, open **Integrations > Purchases**, choose **Adapty**, and create separate Sandbox and Production connections.
2. Configure each URL in **Adapty > Integrations > Webhooks** and select the three supported events.
3. Configure an Authorization value in Adapty and paste that exact full header value into Growth Rail.
4. Identify the same stable app user and optionally store the opaque token as a custom profile attribute:
```ts
const { getAttributionToken } = useGrowthRail();
await adapty.identify(currentUser.id);
const token = await getAttributionToken();
if (token) {
await adapty.updateProfile({
customAttributes: { gr_attribution: token },
});
}
```
Growth Rail deduplicates on `profile_event_id` and ignores renewals. If an Adapty app permits only one webhook URL and it is already occupied, use a webhook fan-out service.
---
# PayPal Integration
Attribute `PAYMENT.CAPTURE.COMPLETED` without giving Growth Rail a PayPal client secret.
## Setup
1. In Growth Rail, open **Integrations > Purchases**, choose **PayPal**, and create a connection for the matching Sandbox or live REST app.
2. Add its URL under **PayPal Developer Dashboard > Apps & Credentials > Webhooks** and select `PAYMENT.CAPTURE.COMPLETED`.
3. Copy the PayPal **webhook ID** into Growth Rail. Do not provide a client ID or client secret.
4. Set the purchase unit `custom_id` to the opaque Growth Rail token:
```ts
createOrder: (_data, actions) => actions.order.create({
purchase_units: [{
amount: { value: total },
custom_id: GrowthRail.getAttributionToken(),
}],
}),
```
Growth Rail verifies PayPal's RSA signature using its allow-listed HTTPS certificate URL and the configured webhook ID. Sandbox and live webhook IDs are different and require separate connections.
---
# Polar Integration
Attribute positive-value Polar orders using `order.paid`, Standard Webhooks signatures, and checkout metadata.
## Setup
1. In Growth Rail, open **Integrations > Purchases**, choose **Polar**, and create a Sandbox or Production connection.
2. In **Polar > Organization settings > Webhooks**, add the generated URL, keep the format set to **Raw**, subscribe to `order.paid`, and configure an endpoint secret.
3. Save the same webhook secret in Growth Rail.
4. For a hosted Polar Checkout Link, append the opaque SDK token as `reference_id`:
```ts
const url = new URL(polarCheckoutLink);
const token = GrowthRail.getAttributionToken();
if (token) {
url.searchParams.set('reference_id', token);
}
window.location.assign(url);
```
Polar propagates `reference_id` into the resulting order metadata. If you create Checkout sessions through the Polar API, pass the token as `metadata.gr_attribution`, your stable user ID as `metadata.gr_user_id`, and optionally set `externalCustomerId` to the same user ID.
Growth Rail validates `webhook-id`, `webhook-timestamp`, `webhook-signature`, and the exact raw body under the Standard Webhooks contract. It accepts only paid, positive-value `order.paid` events and never falls back to customer email.
Use separate Polar endpoints and Growth Rail connections for sandbox and production. Live one-time purchase, immediate subscription, free-trial conversion, retry, and secret-rotation tests remain required before production certification.
---
# Authentication
Growth Rail uses two different credentials depending on where your code runs. The client-side SDK uses a **Project Secret**, while server-to-server REST API calls use an **API Key**.
## Credentials Overview
| Credential | Prefix | Header | Where to use |
| --- | --- | --- | --- |
| **Project Secret** | `sk_` | `X-GrowthRail-ProjectSecret` | Client-side SDK only (`@growth-rail/react`, `@growth-rail/core`) |
| **API Key** | `grak_` | `Authorization: Bearer` | Server-to-server REST API calls only |
## Project Secret
Your Project Secret (prefixed `sk_`) is used exclusively by the client-side SDK. Pass it during initialization — the SDK attaches it automatically to every request via the `X-GrowthRail-ProjectSecret` header. Find it in the dashboard under **Project Settings**.
```http
X-GrowthRail-ProjectSecret: sk_a1b2c3d4e5f6g7h8...
```
> **Safe to ship in client code.** The Project Secret is designed to live in your client bundle. Secure it by keeping your Allowed Origins list tight — only add domains you control.
## API Keys
API Keys (prefixed `grak_`) are used for server-to-server REST API calls. Create and manage them in the dashboard under **Organisation → API Keys**. Include the key in the `Authorization` header using the Bearer scheme.
```http
Authorization: Bearer grak_your_api_key_here
```
> **Never expose API keys in client-side code.** API keys are for server-to-server use only. Keep them in environment variables on your backend.
---
# REST API (v1)
Growth Rail exposes a versioned Public REST API for server-to-server integration. The API is described by an OpenAPI specification, which is the authoritative, machine-readable source for every endpoint, request/response schema, and status code.
## OpenAPI specification
The interactive API reference in the docs site is rendered from the project's OpenAPI document. Point your tools (LLMs, code generators, HTTP clients) at the OpenAPI JSON to get the full, always-current endpoint catalogue.
The OpenAPI document URL is configured at build time via the `VITE_OPENAPI_URL` environment variable (development default: `http://localhost:5035/openapi/public.json`). Load this document to enumerate the available endpoints, parameters, and schemas.
## Authentication
All Public REST API requests are authenticated server-to-server with an **API Key** using the Bearer scheme:
```http
Authorization: Bearer grak_your_api_key_here
```
Create and manage API keys in the dashboard under **Organisation Settings**. See the Authentication page for full details on API keys vs. the client-side Project Secret.
## Conventions
- Base protocol: HTTPS, JSON request and response bodies.
- Auth: `Authorization: Bearer grak_...` header on every request.
- Errors are returned with standard HTTP status codes; `401 Unauthorized` indicates a missing/invalid key or a disallowed origin.
Refer to the OpenAPI specification above for the definitive list of resources and operations.