A person who arrives through a referral link should know, before anything else, what their friend's link gives them. The friend's welcome says it: the gift as the headline, one line of conditions with its deadline, one button, and, whenever the person who invited them is rewarded, a line that says so.
Jane invited you: 1 month free For new customers. Use it within 30 days. Jane gets a reward if you buy.
There are three ways to show it, and they all read the same offer:
- The tag. Version 2 of the referral tag draws a small banner on your own site. One attribute, no code.
- Your own screen.
BoltClawReferral.offer()in the browser, orGET /v1/referrals/offers/{code}from your server, returns the offer as data. - Our welcome page. For a site that cannot add a tag: a person opening
boltclaw.ai/r/<code>sees a one-screen welcome with a Continue button to your product, instead of going straight there.
What the friend gets comes from the program's reward rules for the friend (see Rewards that are not cash): a discount, credit, a free period, a perk or a gift card. A program with no reward for the friend has no offer, and nothing is drawn.
The tag, version 2
v2.js does everything the first tag does, unchanged: it keeps the referral
code from the link in the same first-party cookie on your own domain, and
gives you BoltClawReferral.code(), attach(form) and clientReferenceId().
v1.js stays where it is and never changes. Everything new in version 2 is
off until you ask for it:
<script src="https://boltclaw.ai/referrals/v2.js" defer
data-welcome="auto" data-payment-links="auto"></script>
| Attribute | What it does |
|---|---|
data-welcome |
bar, corner or sheet draws the welcome in that style; auto uses the style you chose in the dashboard; none (the default) draws nothing. |
data-payment-links |
auto adds the code to every Stripe Payment Link on the page as client_reference_id, and the friend's discount code as prefilled_promo_code once the offer is read, and sets client-reference-id on every <stripe-buy-button>. A value you already put there is left alone. |
data-accept-params |
Also read a code from these link parameters, for links made by another referral tool before you moved: any of ref, via, fpr, lmref and tolt_referral. |
data-consent |
required: read the code but store nothing and ask nothing until your consent banner calls BoltClawReferral.consent(true); consent(false) forgets it. auto (the default) behaves as version 1. |
data-cookie-days, data-attribution, data-param |
As in version 1. |
The one request. The tag makes no network request at all unless you turned
the welcome or the offer on and the visitor arrived with a referral code in
the page's own address. Then it makes exactly one GET to
boltclaw.ai/api/referrals/offer, carrying that code and nothing else: no
cookie (credentials: "omit"), no visitor id, no page address. The answer may
be cached for five minutes. The tag loads no script, font or image, sends no
beacon, and sets nothing on boltclaw.ai.
The banner is drawn in a closed shadow root, so your page's styles cannot
reach it and its own cannot leak out. Every word is set as text, never parsed
as markup. It uses your accent colour and corner radius and the system font,
respects reduced motion, has a focusable Close button, and remembers a close
for that code in localStorage.
Headless. BoltClawReferral.offer() returns a promise of the offer (below)
or null, so your own component can draw it. It makes the same one request,
once, however often you call it.
The offer
{
"offer": {
"inviter": "Jane",
"headline": "Jane invited you: 1 month free",
"gift": "1 month free",
"conditions": "For new customers. Use it within 30 days.",
"disclosure": "Jane gets a reward if you buy.",
"discount": { "kind": "percent", "value": 10000, "currency": null, "unit": null, "duration": "months", "months": 1, "text": null, "code": "JANE20" },
"stripe": { "promotion_code": "promo_1Q2w3E4r5T6y7U8i" },
"cta": { "label": "Start your free month", "url": "https://www.example.com/signup" },
"look": { "style": "bar", "position": "top", "accent": "#1a2b3c", "radius": 12, "theme": "auto" }
}
}
discount.kindispercent(valuein basis points),amount,creditorgift_card(valuein minor units ofcurrency),free_period(valuea count ofunit) orperk(textin your words).discount.codeis the referrer's own discount code, when they have one: pass it to Paddle, Lemon Squeezy, Shopify or anything else that takes a code.stripe.promotion_codeis the referrer's promotion code in your own Stripe, when Bolt Claw knows it (see Stripe codes and credits).disclosureis present whenever the program rewards the person who invited them, and you cannot remove it while it does. Consumer-protection rules expect a referral to say so.
One answer for every dead code. A code that does not exist, is archived,
belongs to a paused program or a banned referrer, or has no reward for the
friend, is answered { "offer": null }, the same in every case and on every
call, so nobody can learn which codes exist or whose they are. Past the
per-address limit (60 a minute) the answer is the same null.
Who may read it. A browser may read the offer only from your product URL's
own host, the hosts you list under Other sites that may show the offer
(allowed_hosts), or the sites your referral panel is
framed on: https, any port (add localhost to the list to read it from
http://localhost while you develop). Any other
site's script cannot read who invited whom.
Whose name is shown
Only a name the person agreed to show:
- one of your own users (a referrer you made through the panel or the API):
the
nameyou sent, while the program's Show the inviter's name is on (welcome_show_name, on by default); - an affiliate: their display name in the program, only if they ticked Show my name to people who use my link when they joined (ticked by default, and changeable any time on their page).
Turning Show the inviter's name off hides every name, an affiliate's included. A discount code that spells a name is left out of an offer that may not show it.
Otherwise the welcome says "A friend invited you". The name is set as text and shown as text; nothing in it can run.
The words and the look
In the dashboard, the program's Welcome tab sets the style and position,
the headline, the conditions line, the button and the welcome page, with a live
preview in light and dark, at desktop and phone width. Through the API, send
welcome_config on PATCH /v1/referrals/programs/{id}:
curl -X PATCH https://api.boltclaw.ai/v1/referrals/programs/$PROGRAM_ID \
-H "Authorization: Bearer $BOLTCLAW_KEY" -H "Content-Type: application/json" \
-d '{"welcome_config": {"style": "bar", "headline": "{name} gave you {offer}", "cta_label": "Start your free month", "cta_url": "https://www.example.com/signup"}}'
{name} is the inviter (or "A friend") and {offer} the gift. Every text is
plain text: angle brackets, control characters and invisible marks are refused,
so not even a future careless renderer could be handed markup. Left out, the
conditions line is "For new customers. Use it within N days.", N being the
program's cookie_days.
At checkout: may this customer have it?
From your server, with your key, ask before you create a checkout:
curl "https://api.boltclaw.ai/v1/referrals/offers/jane-20?customer_id=user-2087&new_customer=true" \
-H "Authorization: Bearer $BOLTCLAW_KEY"
{ "offer": { "...": "as above" }, "eligible": false, "reason_code": "self_referral",
"reason": "You can't use your own referral link.", "coupon": null }
reason_code |
Means | Say instead of an error |
|---|---|---|
unknown_code |
No live link of yours has this code. | That referral link isn't active. |
program_paused |
The program is paused. | Referrals are paused right now. |
self_referral |
The customer is the referrer. | You can't use your own referral link. |
already_referred |
Another referrer already has this customer. | Your account was already referred by someone else. |
not_new |
You said the customer is not new. | This offer is for new customers. |
With Stripe Checkout, pass the friend's discount yourself: discounts: [{ coupon }] with the coupon (given only when eligible is true), or [{ promotion_code }] with
offer.stripe.promotion_code, beside client_reference_id. The browser never
decides the discount.
A code entered after sign-up. Your own "Enter a referral code" field reports
a lead with the code, as before. A code that arrives more than the program's
late_code_days (14 by default) after the customer was first seen is not
applied, and the conversion says referral_refused: "late_code".
"Invited by Jane". GET /v1/referrals/programs/{id}/customers/{external_id}/referral
answers who invited one of your customers (invited_by, a name only with
consent), when and how, for a line in their settings.
Ask them to invite their friends
With Invite after purchase on (invite_after_purchase, on for the
"Refer a friend" template), a friend's first payment makes them a referrer of
their own when you named the customer by your id (customer.id). The
conversion's answer carries referee_referrer: { link, code } and
referral.referee.invite_ready tells your server, so a thank-you page can ask
straight away: open the referral panel with
mode="thank-you" for a compact "Now give your friends 1 month free" card.
Ask at a good moment, not every time: the panel's Not right now is
remembered on our side for ask_snooze_days (30 by default), and the View says
ask.should_ask.
Our welcome page
Turn on Show a welcome page on your links (welcome_page) and a person
opening /r/<code> sees one screen, "Jane invited you to Acme: 1 month free",
with a Continue button to your product link and the attribution kept. Link
previews in chat apps still get the preview card. Like every /r/ page it is
noindex, loads no third-party script and sets no cookie.