Start here
Hosted, widget or API
Choose where customers share. Keep their identity verified.
Let customers follow their referrals
The hosted page, widget and prompt show confirmed referrals, the date attribution was recorded, qualifying payments and each reward's current state. These are not link-click or free-signup timestamps. A referral can appear before it earns a reward. Referrals use anonymous references, not friends' emails or billing identifiers.
The referrer response includes the existing progress totals and an additive referral_progress object. For a read-only refresh, call POST /api/v1/referrers/progress from your server with the same bearer key and {"customer_id":"cus_…","cursor":null}. It does not enrol customers or issue rewards.
referral_progress.items contains reference, attributed_at, attributed_label, payments and reward. Payments include the recorded count, a required milestone before earning (null once a reward exists), and a display label. Rewards include earned, status, label, detail, the earned reward's stored description, hold_ends_at and issued_at. A hold-end date is not a promised application date.
Pages contain at most 10 referrals, newest first, ordered by attribution date and a stable tie-breaker. Pass pagination.next_cursor or pagination.previous_cursor unchanged in the next request; null means there is no page in that direction. Cursors are positions, not access credentials. Each request still needs your key and the authenticated customer's trusted Stripe ID. Invalid cursors return HTTP 400, invalid_cursor. Keep responses private and uncached.
Reward status values are qualifying, held, waiting_subscription, ready, processing, paused, failed, cancelled and issued. Earned does not mean applied; only issued confirms application. Use the supplied safe labels and details rather than displaying provider errors.
Verify the customer, not a browser ID
Every customer must exist in your connected Stripe account and mode. Free, trial and paid customers may enrol. An inactive customer cannot regain access by signing in or sending an event.
For API and widget access, authenticate the user in your app first. Look up their Stripe customer ID from your trusted server-side user record. Never accept an arbitrary customer_id from a browser form or URL.
Hosted page: no integration required
Copy your referral-page URL from Customer access. It looks like https://refersprout.com/r/your-business.
The customer enters the email on their Stripe account. For a matching customer we send a sign-in link, valid for 20 minutes. The response does not disclose whether an email matched. The verified page shows their own code, link, terms and progress.
This is an access email, not an ask campaign. Automated customer ask emails are not available. Private access and widget pages are not indexed by search engines.
Widget: a referral card inside your app
Call POST /api/v1/widget-tokens on your server with the authenticated customer's ID. It enrols an eligible customer if needed. A successful response is HTTP 201 with url, token and expires_at.
// Server only. user comes from your authenticated session.
const response = await fetch(
"https://refersprout.com/api/v1/widget-tokens",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.REFERSPROUT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ customer_id: user.stripeCustomerId }),
},
);
if (!response.ok) {
// Handle programme_paused, customer_deactivated, etc.
throw new Error("Referral access is unavailable");
}
const { url, expires_at } = await response.json();
// Render only this customer's URL. Never cache it publicly.{
"token": "SIGNED_WIDGET_TOKEN",
"url": "https://refersprout.com/widget/your-business?token=SIGNED_WIDGET_TOKEN",
"expires_at": "2026-10-27T12:15:00.000Z"
}Render the returned URL for that customer only. For React:
<iframe
src={url}
title="Refer a friend"
allow="clipboard-write"
style={{ width: "100%", maxWidth: 480, height: 420, border: 0 }}
loading="lazy"
/>Fetch a fresh URL each time the page is rendered; it expires after 15 minutes. Keep allow="clipboard-write" so copying works inside the frame. The frame may scroll vertically when more content is shown.
HTTPS sites and local development may embed the widget. Widget responses use frame-ancestors https: http://localhost:* http://127.0.0.1:*, Referrer-Policy: no-referrer and Cache-Control: private, no-store. Treat the signed URL as a customer-specific secret; do not put it in analytics.
For a timely custom ask rather than the always-available card, use the moment and report flow. A widget session token is not an ask-report token.
API: your own referral experience
POST /api/v1/referrers enrols the verified customer and returns their link and progress. A new enrolment returns HTTP 201; an existing active enrolment returns HTTP 200 with created: false. Repeat access does not create another referrer or reset ask history.
curl https://refersprout.com/api/v1/referrers \
-H "Authorization: Bearer $REFERSPROUT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"customer_id":"cus_example"}'{
"created": true,
"referrer": {
"customer_id": "cus_example",
"referrer_id": "00000000-0000-4000-8000-000000000001",
"code": "SAM8K2QX",
"link": "https://merchant.example/join?prefilled_promo_code=SAM8K2QX&client_reference_id=rs_SAM8K2QX",
"rewards": {
"you_get": "$10.00 account credit",
"friend_gets": "20% off one invoice",
"friend_reward_type": "percent_off",
"after_payments": 2,
"hold_days": 14
},
"progress": {
"friends_joined": 0,
"friends_qualified": 0,
"rewards_earned": 0,
"rewards_issued": 0
}
}
}link is null until a landing URL is saved. rewards describes the current programme terms. Progress counts the customer's non-revoked referrals and referrer rewards, not their friends' rewards.
The API also echoes customer_id to your authenticated server. Moment views do not include it. Expose only the data your signed-in customer needs, never another customer's response.
Use PATCH for explicit participation changes. See access errors before treating every non-200 as a retry.