# The friend's welcome

Tell a person who arrives through a referral link what it gives them, on your site or ours, and check it at checkout.

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, or
  `GET /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](https://boltclaw.ai/docs/rewards-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:

```html
<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

```json
{
  "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.kind` is `percent` (`value` in basis points), `amount`, `credit` or
  `gift_card` (`value` in minor units of `currency`), `free_period` (`value` a
  count of `unit`) or `perk` (`text` in your words). `discount.code` is 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_code` is the referrer's promotion code in your own Stripe,
  when Bolt Claw knows it (see [Stripe codes and credits](https://boltclaw.ai/docs/referral-stripe-codes)).
- `disclosure` is 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](https://boltclaw.ai/docs/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 `name` you 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}`:

```bash
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:

```bash
curl "https://api.boltclaw.ai/v1/referrals/offers/jane-20?customer_id=user-2087&new_customer=true" \
  -H "Authorization: Bearer $BOLTCLAW_KEY"
```

```json
{ "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](https://boltclaw.ai/docs/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.

<!-- https://boltclaw.ai/docs/referral-welcome as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
