RewardBase — Widget Test Page

Connecting...
Workspace:  ·  Widget: Loading...  ·  Referral API:  ·  Widget JS:
Share with client (ngrok):   ·  Run pnpm dev:next + pnpm run dev:webhook (tunnels port 3000)
1 — Identify User
2 — Widget Controls

Open the widget panel to see the user's in-app action list, activity tab, and program details.


Checking widget settings...
3 — Event Fields (shared by SDK + API)
4a — SDK (Browser)

Uses Rewardbase.track() — fires from the browser, no API key needed. Identify a user first (step 1).

4b — API (Server-side / x-api-key)

Uses POST /api/v1/events with an API key. Get the key from Dashboard → Settings → API Keys.

Activity Log
No activity yet. Identify a user and fire an event.
1 — Widget Home truncate & reorder check

Open the widget to verify the referral entry appears on the home list (title truncation, ordering vs in-app actions), the invite screen, the referral link field, and the referral list UI.


2 — Attribution cross-domain · first/last touch · window

Land on the page with the referral param (default ?ref=CODE) to record a touch. First/last-touch resolution & the attribution window come from the active program config. Storage is dual cookie (root-domain, cross-subdomain) + localStorage.


Attribution window test — backdates rb_referral_code_set_at 31 days into the past and reloads the page without the ref param. The widget's captureAttribution() then detects the expired touch and clears the stored code. Afterwards, clicking Identify (SDK signup) in section 3 should return skipped: true, reason: no_referral_code. Requires a referral code to be in storage first — use Simulate touch above.

2c — Cross-Subdomain Tracking verify cookies span subdomains

The widget writes attribution cookies with domain=.example.com (the root domain), so the same cookie is readable on www.example.com, app.example.com, or any other subdomain — no extra setup required. Run diagnostics to confirm what domain is being used and what values are currently stored.

Current hostname
Cookie domain (set on all cookies)
Cookies shared with

How to verify with a real second subdomain
1. Set a referral code above — click Fetch real code from API then Simulate touch.
2. Click Run diagnostics and confirm rb_referral_code shows source: "both" (cookie + localStorage).
3. Click Copy test URL for another subdomain, change the host in the copied URL to a different subdomain of the same domain (e.g. swap wwwapp), and open it.
4. On that subdomain, click Run diagnostics again — rb_referral_code should still read from cookie, proving it crossed the subdomain boundary.
5. From that second subdomain, run Identify (SDK signup) in section 3 — it will record the correct referral code, completing the cross-subdomain flow.

3 — Signup Tracking invitee addition · fingerprint

Calls rewardbase.identify(email, userId, name) — the SDK reads the stored referral code and posts to /api/referral/<ws>/signup with the visitor-id fingerprint. The response's attributionStatus drives the invitee discount & fraud message. Set the code in section 2 first.

4 — Action Tracking attributes

rewardbase.track(actionKey, { userId, email, name, attributes }). In-app first, then referral qualifying-action fallthrough. Only fires for signup_and_action programs.

4b — Action via Server API x-api-key fallthrough

POST /api/v1/events with an API key. When no in-app program matches the key, it now falls through to referral qualifying-action tracking (channel: "referral").

5 — Fraud Test Cases ALLOW · FLAG · BLOCK

Each preset runs a direct signup with a crafted identity to trigger one fraud rule. The attributionStatus in the response shows the decision: qualified (allow), flagged (flag), disqualified (block). Rules marked opt-in must be enabled in Settings → Fraud first. Set a referral code (section 2) before running. For Blocked country and VPN / Tor presets, this page injects fake IP intel into the backend request so the rule fires even from localhost — no real VPN needed.

6 — Invitee Banner with/without name · with/without profile icon

The banner renders at the top of the host page when a valid referral code is stored and the program banner is enabled. Title substitutes {referrer_name}; the avatar falls back to the invite icon when the referrer has no profile image. Pass a code with a known referrer to see the name + avatar; pass an unknown code to see the no-name / icon fallback. Reload after changing the code so the widget re-captures.

Activity Log
No activity yet. Simulate a touch, then identify an invitee.
How to use this page read once, then follow steps 1–4 below

Before you start (one-time setup)

Payment provider Dashboard → Settings → Payment → connect Lemon Squeezy, Dodo Payments, or Stripe by pasting the provider API key. (Use a test-mode provider account so checkouts are free.)
Workspace API key Dashboard → Settings → API Keys → create a key. Paste it into step 1 below — it authorizes the create-webhook and checkout calls.
Test product Create at least one product (or active price for Stripe) in your provider (test mode). Step 3 lists these so you can pick what to “buy”.
Active referral program Dashboard → Programs → open your referral program → status must be Active. The program model must be signup_and_trial (trial/subscription checkout) or signup_and_purchase (purchase checkout).
workspaceId in URL The URL must contain ?workspaceId=<your-id>. Copy it from the dashboard URL or Settings → Workspace.
ngrok (client testing) Run pnpm dev:next + pnpm run dev:webhook, then share the Share with client URL from the bar above (e.g. https://<subdomain>.ngrok-free.dev/widget/index.html?workspaceId=…). API + widget load from the same ngrok origin automatically.
Referral code in storage Switch to Referral mode → section 2 → set a Referrer User ID → click Fetch real code from API, then Simulate touch. Return here. Step 2 reads the stored code automatically.

Step-by-step process

Step Action What happens How to verify it worked
1 — Webhook Select provider, fill User ID + Email, paste the workspace API key, click Create / verify webhook. Calls the provider API to register RewardBase's referral webhook in test mode (or reuse it if present) and stores the signing secret for verification. Status shows Webhook created or already existed with the event list. The webhook now appears in your provider dashboard.
2 — Signup Click Record invitee signup. Posts to /api/referral/<ws>/signup with the invitee identity + the stored referral code. Creates an attribution record linking this invitee to the referrer. Status box shows Signup recorded. attributionStatus should be signed_up (or qualified for signup-only programs). If you see invalid_code → go to Referral mode and fetch a real code first.
3 — Checkout Click Load products, pick a product + mode, click Generate test checkout & open. Pay on the opened page with the provider test card. Creates a real test-mode checkout via the provider API with your invitee identity in metadata. Paying triggers the provider's real webhook to RewardBase. A provider checkout page opens. After paying with the test card you are redirected to a success page.
4 — Listen Open Dashboard → Referrals and find this invitee's record. The provider webhook (checkout.session.completed / order_created / payment.succeeded for purchase; customer.subscription.created / subscription_created / subscription.active for trial/subscription) hits the receiver, which verifies the signature, matches the invitee and qualifies the referral. Record status becomes Qualified and the referrer reward appears. A later refund/cancellation rolls it back to cancelled.

Webhook events by provider

Each referral program uses one model: signup_and_purchase listens for purchase rows; signup_and_trial listens for trial/subscription rows. A provider may send multiple webhooks for one checkout — Reward Base only acts on the event that matches your program model, so the same referral is never qualified twice.

Task Dodo Payments Lemon Squeezy Stripe
Purchase tracking payment.succeeded order_created (one-time)
subscription_payment_success (subscription)
checkout.session.completed (amount > 0)
checkout.session.async_payment_succeeded
charge.succeeded
invoice.paid
invoice.payment_succeeded
payment_intent.succeeded
Trial tracking
7-day free trial, card required
subscription.active subscription_created checkout.session.completed (amount = 0)
customer.subscription.created
Trial tracking
$1 trial
payment.succeeded subscription_payment_success checkout.session.completed (amount > 0)
invoice.paid
Trial tracking
Post-paid subscription
subscription.active subscription_created checkout.session.completed
customer.subscription.created
Trial cancelled /
subscription cancelled
subscription.cancelled
subscription.on_hold
subscription_cancelled
subscription_paused
subscription_expired
customer.subscription.paused
customer.subscription.deleted
subscription_schedule.canceled
Refund refund.succeeded order_refunded (one-time)
subscription_payment_refunded (subscription)
refund.created
charge.refunded
credit_note.created
Chargeback / dispute dispute.opened order_refunded (one-time)
subscription_payment_refunded (subscription)
issuing_dispute.created
charge.dispute.created

Common questions

Can the same checkout qualify a referral twice?
No. Providers often send more than one webhook per checkout. Reward Base picks the single event that matches your program model and ignores the rest. Each webhook is also stored by provider event ID — replays return duplicate, not a second qualification.

Purchase vs trial — do they overlap?
No. You configure one program model per referral program:
signup_and_purchase → only purchase events qualify the referral.
signup_and_trial → only trial/subscription events qualify the referral.
A trial webhook on a purchase program returns irrelevant. A purchase webhook on a trial program returns irrelevant. They never compete.

Dodo: why are both payment.succeeded and subscription.active in the table?
They apply to different checkout types, not the same referral twice.
One-time product → Dodo sends payment.succeeded → qualifies on a purchase program.
Subscription or trial → Dodo may send payment.succeeded and subscription.active. Reward Base uses subscription.active for trial/subscription programs. The payment.succeeded from that same checkout is not used for qualification.
Think of it as two doors — only the door that matches what the customer bought and your program model opens.

Stripe: official event names vs common mistakes
Verified against Stripe event types:
invoice.refundednot a real Stripe event. Use charge.refunded or refund.created.
invoice.credit_note_creatednot a real Stripe event. Use credit_note.created.
issuing_dispute.created is for Stripe Issuing (card program) disputes; standard Checkout chargebacks use charge.dispute.created.
invoice.paid and invoice.payment_succeeded both exist; Reward Base treats both as purchase events when amount > 0.

Stripe: is customer.subscription.created a purchase or a trial?
Neither by name alone — it depends on the subscription status Stripe sends:
status=trialing → free trial started → qualifies a signup_and_trial program.
status=active → paid subscription started → qualifies a signup_and_trial program.
One-time purchases use checkout.session.completed or invoice.paid on a signup_and_purchase program instead.

Lemon Squeezy: why both order_created and subscription_payment_success?
Same idea as Dodo — different product types:
order_created → one-time purchase.
subscription_payment_success → subscription payment.
Reward Base ignores order_created when it belongs to a subscription checkout so it does not double-count.

What must match between checkout and signup?
The invitee User ID or Email in checkout metadata must be the same identity recorded in step 2. If they differ, the webhook returns no_attribution even when the event type is correct.

Response decoder — what each status means

status / attributionStatus Where it appears What to do
recorded / signed_upSignup (step 2)Good — attribution exists. Now fire a payment event (step 3 or 4).
processed / qualifiedPayment eventsGood — referral qualified, reward dispatched. Check Dashboard → Referrals.
duplicatePayment eventsGood (idempotency working) — this exact webhook was already processed.
no_attributionPayment eventsSignup not found for this User ID / Email. Run step 2 first using the same identity.
irrelevantPayment eventsEvent type is not mapped for referrals (e.g. trial button on a purchase program). Use the correct step for your program model.
invalid_codeSignup (step 2)No referral code stored. Go to Referral mode → section 2 → Fetch real code → Simulate touch, then return here.
flaggedSignup or paymentFraud rule triggered but not blocked. Referral needs manual review in Dashboard → Referrals.
disqualifiedSignup or paymentBlocked by fraud rule. Most common cause: same User ID or Email as the referrer (self-referral). Use different invitee credentials.
cancelledAfter cancel/refund/disputeExpected when you trigger a refund/cancel/dispute. Reward rolled back.
1 — Connect & Create Webhook API entered → create webhook

Connect the provider in Settings → Payment first. Then paste a workspace API key (Settings → API Keys) below and click Create / verify webhook. This calls the provider API to register RewardBase's referral webhook in test mode (or reuses it if it already exists) and stores the signing secret so inbound events are verified.

2 — Record Invitee Signup prerequisite for attribution

A payment webhook only qualifies a referral if the invitee already signed up with a stored referral code. This records that signup using the identity above. If no code is stored yet, switch to Referral mode → section 2 to fetch a real code, then return here.

3 — Simulate Checkout create test checkout → pay with dummy card

Loads the products from your connected store, then creates a real test-mode checkout link with the invitee identity embedded in metadata. Open it and complete payment with the provider's test card (Stripe / Lemon Squeezy: 4242 4242 4242 4242; Dodo: 4111 1111 1111 1111). The provider then fires the real webhook to RewardBase, which qualifies the referral.

4 — Listen to Webhook payment completed · subscription/trial started

After paying, the provider sends the webhook (checkout.session.completed / order_created / payment.succeeded for purchase; customer.subscription.created / subscription_created / subscription.active for trial/subscription) to /api/webhook/<provider>/referral/<ws>. RewardBase verifies the signature, matches the invitee and qualifies the referral. Verify in Dashboard → Referrals — the record should turn Qualified and the referrer reward should appear. Refunds / cancellations roll it back automatically.

Activity Log
No activity yet. Record a signup, then fire a payment event.
How to use this page test both sides of a referral reward: referrer + invitee

A referral program rewards two people. The Referrer Reward goes to the existing user who shared the link; the Invitee Reward goes to the new user who joined. Both are dispatched automatically the moment an attribution reaches qualified. This tab lets you pick a referrer + invitee, trigger qualification, then verify each reward side independently. Set ?workspaceId=<your-id> in the URL first.

Side Type What happens on qualify How to verify here
Referrer Reward webhook Signed Svix webhook (reward.issued) with context.referralRewardType: "webhook", participant = referrer. Check your webhook endpoint / Dashboard → Referrals (attribution → completed).
wallet Referrer's wallet credits increase by the configured points. Section 3 — check referrer credits before & after qualifying.
Invitee Reward webhook Signed Svix webhook with context.referralRewardType: "invitee_webhook", participant = invitee. Check your webhook endpoint / Dashboard → Referrals.
discount A pre-configured coupon code becomes available to the invitee after signup. Section 4 — read the invitee discount code from program config.
Prerequisites: an active referral program (Dashboard → Referrals) with a referrer reward + invitee reward configured. For webhook rewards configure Settings → Webhooks; for the discount invitee reward set a coupon in the editor. A valid referral code is required — fetch one in section 1 (or set it in the Referral tab). Share this page with clients via ngrok (pnpm run dev:webhook) using the URL in the bar above.
1 — Referrer owns the link · receives referrer reward

The existing user who shares the referral link. Fetch their real code via GET /api/widget/<ws>/referral. If the referrer reward is wallet, note their current credit balance now to compare after qualifying (section 3).

2 — Invitee & Qualify new user · triggers both rewards

The new user joining via the link. Record signup creates the attribution — for a signup_only program this qualifies immediately and dispatches both rewards. For signup_and_action also fire the qualifying action. For trial / purchase models use the Payments tab to complete a checkout.

3 — Referrer Reward webhook · wallet

Wallet reward: re-check the referrer's credits below — they should increase by the configured points (note: programs with a hold period on trial/purchase models delay this until the window passes). Webhook reward: RewardBase POSTs reward.issued to your endpoint with the payload shown.

Expected referrer webhook payload

{
  "eventType": "reward.issued",
  "workspaceId": "ws_…",
  "context": {
    "type": "referral",
    "programId": "prog_…",
    "attributionId": "attr_…",
    "referrerId": "part_referrer_…",
    "inviteeId": "part_invitee_…",
    "referralRewardType": "webhook"
  },
  "reward": { "id": "rule_…", "instanceId": "trigger_…" },
  "participant": { "id": "<referrer userId>", "email": "referrer@…" },
  "metadata": { "fields": { …configured… }, "tags": [ …configured… ] }
}
4 — Invitee Reward webhook · discount

Discount reward: read the invitee's coupon code from GET /api/referral/<ws>/program-config (returns discountCouponCode; hidden once the invitee is disqualified). You can also read it through the loaded SDK via rewardbase.getDiscountCode(). Webhook reward: RewardBase POSTs reward.issued with referralRewardType: "invitee_webhook".

Expected invitee webhook payload

{
  "eventType": "reward.issued",
  "workspaceId": "ws_…",
  "context": {
    "type": "referral",
    "programId": "prog_…",
    "attributionId": "attr_…",
    "referrerId": "part_referrer_…",
    "inviteeId": "part_invitee_…",
    "referralRewardType": "invitee_webhook"
  },
  "reward": { "id": "rule_…", "instanceId": "trigger_…" },
  "participant": { "id": "<invitee userId>", "email": "invitee@…" },
  "metadata": { "fields": { …configured… }, "tags": [ …configured… ] }
}
Activity Log
No activity yet. Fetch a referrer code, qualify an invitee, then verify each reward side.