Participation
Moments and asks
Send a trusted event, show a timely ask and report the outcome.
Configure a moment you can verify
In Programme, save an enabled positive moment named project_completed. A moment is a business event your server knows happened, not a generic page view.
Names match exactly, including case. Use 1–80 characters, starting with a letter; letters, numbers, spaces, underscores, dots and hyphens are allowed. Negative moments record feedback and suppress asks instead of asking.
Project completed, teammate invited or NPS 9–10 are events you supply. ReferSprout does not automatically watch your product or run an NPS integration. Default wording uses your actual reward, milestone and hold; if you customise it, keep it accurate when terms change. Wording is plain text, never HTML.
Send the event from your server
First enrol the customer. Then call POST /api/v1/moments after authenticating them and confirming the event in your own system. This example uses receipt time so a copied example cannot accidentally become stale.
curl https://refersprout.com/api/v1/moments \
-H "Authorization: Bearer $REFERSPROUT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"customer_id":"cus_example","moment":"project_completed","event_key":"project:123:completed:v1"}'| Field | Rule |
|---|---|
customer_id | Your trusted server-side Stripe customer mapping; already enrolled and verified again in the connected account. |
moment | Exact saved definition name. |
event_key | Stable ID for one business event, 1–128 ASCII letters/numbers or _.:-. Unique across your account/mode, not just this customer. |
occurred_at | Optional ISO timestamp. Defaults to receipt. Within the past 7 days or up to 5 minutes in the future; future values are clamped to receipt time. |
account_created_at | Optional trusted account creation time from your server. Omit when unknown. Future dates are rejected. The first known age persists. |
Send only these fields. Unknown fields are rejected; the JSON body is limited to 4,096 bytes. Do not attach raw customer payloads or arbitrary event metadata.
Read ask or skip before rendering
A new event returns HTTP 201, a duplicate HTTP 200. IDs, tokens and times are generated per decision.
{
"decision_id": "00000000-0000-4000-8000-000000000002",
"created": true,
"action": "ask",
"reason": null,
"help": null,
"expires_at": "2026-10-27T12:02:00.000Z",
"report_token": "SIGNED_REPORT_TOKEN",
"wording": "Give a friend 20% off one invoice. You can earn $10.00 account credit after 2 qualifying payments, with a 14-day hold.",
"view": {
"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
}
}
}When action is skip, do not show a prompt. view, wording and report_token are null. The response includes a reason and actionable help:
{
"decision_id": "00000000-0000-4000-8000-000000000003",
"created": true,
"action": "skip",
"reason": "recent_copy",
"help": "Wait until the quiet period after copying ends.",
"expires_at": "2026-10-27T12:02:00.000Z",
"report_token": null,
"view": null,
"wording": null
}A positive event over five minutes old is stored but skipped as stale_event. A delayed negative event starts its quiet period at receipt, not at its old event time. See all skip reasons.
Report what was actually shown or copied
An approved decision reserves a slot for 120 seconds. It does not count as shown yet. Render only a current approved decision and call POST /api/v1/ask-reports on actual presentation. Hide or cancel the prompt if the report is refused.
curl https://refersprout.com/api/v1/ask-reports \
-H 'Content-Type: application/json' \
-d '{"token":"SIGNED_REPORT_TOKEN","outcome":"shown"}'{
"accepted": true,
"changed": true,
"context_token": null
}In your own frontend, forward real outcomes through your own same-origin server route to this endpoint. Do not assume cross-origin browser requests to ReferSprout are enabled. Your route must preserve the signed token, not accept an arbitrary customer ID.
Send outcome: "copied" only after a successful copy, and outcome: "dismissed" when dismissed. A first dismissal also counts as shown; copy requires an accepted shown or dismissed report first. Retried reports are deduplicated: changed: false means it was already recorded.
Shown, copied and dismissed each count at most once per decision. Reports recheck current customer participation, moment, pause, negative feedback and ask rules. A stale reservation cannot bypass a new negative event or deactivation.
The report token lasts 15 minutes from decision creation; this does not extend the two-minute deadline for the first shown/dismissed report. A duplicate consumed or expired event returns decision_used_or_expired, never a new reservation. Only a genuinely new business event may request another decision.
Carry optional prompt context
Built-in prompts are hosted by ReferSprout in the widget/SDK frame. The resolver prepares signed context before the first clipboard write. Preparation records no copy, visit or attribution; only a successful copy with an accepted copied report activates it. Failed copying or a refused report does not. The SDK sends the report token through origin-checked postMessage, then the frame posts it to the resolver, keeping the report bearer out of URLs. The copied link adds optional ask context, not a Stripe customer ID or the report token.
For custom API integrations, an accepted copied report returns context_token, valid for 30 days from decision creation. A duplicate copy returns the same token. Your integration can carry this as rs_ask on the shared landing URL, preserving client_reference_id and any prefilled promotion code.
On the merchant landing page, POST /api/v1/ask-visits with {"context_token":"SIGNED_CONTEXT_TOKEN"} records at most one observed visit per copied decision. HTTP 200 returns {"changed":true}; duplicates return false. This is not a visitor count.
Your authenticated server may pass refersprout_ask in Checkout Session, subscription or invoice metadata. Or attach it after the referral webhook through:
curl https://refersprout.com/api/v1/referral-context \
-H "Authorization: Bearer $REFERSPROUT_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"customer_id":"cus_referred_example","context_token":"SIGNED_CONTEXT_TOKEN"}'Here customer_id is the referred friend, not the referrer. HTTP 200 returns changed. A missing referral is 409 referral_not_found; retry after its webhook. Context only attaches when account, mode and winning referrer match, and the referral was created at or after the copy. It never changes first attribution, promotion-code priority or rewards.