> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cello.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Pre-launch Testbook

> A step-by-step testbook to validate your Cello integration end to end: confirm each component is healthy, check your events are correct, run the manual front-end checks, and launch your program.

This testbook walks you through testing your Cello integration on your own, from first event to launch.

By the end you should have:

* The **referral widget** and **attribution library** connected
* **Signups** and **purchases** tracked, with **events validated** as correct
* The **manual front-end checks** done that the portal can't see for you
* Everything ready to **launch your program**

This is the full testbook behind the **View full test book** link in the launch checklist and on the **Technical integration** page.

## Integrate, test, and launch

You integrate, test, and launch all from your **main account**. While you build and test, the account stays in **pre-launch (testing) mode**, so everything in this testbook runs safely against your real product - nothing reaches your referrers until you go live.

| Behavior                               | Pre-launch (testing mode)                                                                                                  | Live                        |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| Events & data                          | Created as **test data** and marked as testing on your dashboards                                                          | Treated as live             |
| Analytics & billing                    | Excluded - test data isn't used in analytics calculations or billing                                                       | Included                    |
| Rewards                                | **Not paid out** to referrers, even if a payout method is set                                                              | Paid out                    |
| Notifications (emails & announcements) | **Sent** to your test users when the triggering activity happens, such as a signup being detected or a reward being issued | Sent to your real referrers |
| Payout delay                           | **Not applied** - test transactions without waiting                                                                        | Applied                     |
| Fraud checks                           | Not run                                                                                                                    | Run                         |

When you **launch your program**, payouts begin, notifications start reaching real referrers, the payout delay applies, and all new events and data are treated as live.

You don't need a sandbox to run this testbook - testing happens directly in pre-launch. A **sandbox** is an optional, fully isolated environment with its own credentials (**My account → Create sandbox**). It's how you test changes once you're live, because launching is not reversible.

## What this testbook covers

Six areas, each testing one part of your integration. Read this table first to see which ones apply to you, then work through them in order.

| Area                                                        | What it proves                                                           | Applies to                             |
| ----------------------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------- |
| [Referral component](#referral-component)                   | The widget boots in your app and the referrer-facing experience is right | Everyone except partners-only          |
| [Notifications](#notifications)                             | The badge, announcements and emails reach referrers                      | Everyone except partners-only          |
| [Capturing the referral code](#capturing-the-referral-code) | A referred visitor's `ucc` is captured and survives to signup            | Everyone                               |
| [Signup tracking](#signup-tracking)                         | Referred signups reach Cello and attribute to the referrer               | Everyone                               |
| [Applying discounts](#applying-discounts)                   | The new-user discount is correct at checkout                             | Only if your offer includes a discount |
| [Purchase tracking](#purchase-tracking)                     | Referred purchases reach Cello with the fields rewards depend on         | Everyone                               |

Then check [Alternative scenarios](#alternative-scenarios) for anything else that describes your setup - a partners-only program, a mobile app, organization-level rewards, Auto Attribution, or report-based attribution.

## Before you start

* You can sign in to the [Cello Portal](https://app.cello.so), and your main account is in **pre-launch (testing) mode**
* The referral component, attribution library, and your signup and purchase events are implemented (see the [Setup Overview](/integration-overview))
* You have test accounts for **every signup method** you support (email/password, Google SSO, Microsoft SSO, and so on) - you'll need a fresh user per signup test
* You have access to your payment gateway's test mode, for example [Stripe test cards](https://stripe.com/docs/testing) or the [Chargebee test gateway](https://www.chargebee.com/docs/2.0/chargebee-test-gateway.html)

**Definition of done:** all four components report **Connected** when you ask your coding tool for your integration status, your events validate as **OK** under **Developers → Events**, and the manual checks in each area pass. That's everything the launch checklist asks for - then you can **launch your program**.

## Where to check things

Two places do most of the work:

<CardGroup cols={2}>
  <Card title="Integration status" icon="heart-pulse" href="/mcp/developers/integration-status">
    High-level health of your four components, through the Cello MCP server. Answers "is everything working?"
  </Card>

  <Card title="Developers → Events" icon="list-check" href="/guides/support/portal/event-feed">
    Per-event, per-field validation. Answers "why did this specific event fail?"
  </Card>
</CardGroup>

## When each surface updates

Cello's surfaces refresh at different speeds, so give each one time before assuming something is broken. These intervals are the same in pre-launch as they are once you're live.

| Surface                                               | When it updates                                                                                                                                                             |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Developers → Events**                               | 1-5 minutes after the event is sent                                                                                                                                         |
| **Integration status**                                | Up to \~10 minutes. A component is **Connected** once at least one successful event arrives within the health window, so re-check if you've only just sent your first event |
| **Dashboards, and the referrer's view in the widget** | Hourly - signups, purchases and rewards are matched and aggregated before they surface                                                                                      |

## Test by area

Each area below follows the same shape: what it needs before you start, what to do, and what you should see when it works.

### Referral component

The referral widget (`cello.js`) booting in your app, plus the referrer-facing experience around it.

**Prerequisite:** the referral component is implemented and **boots at login** - not lazily when the launcher is first clicked. Booting at login is what makes notifications and rewards ready the moment a referrer signs in, and it's what produces the `booted` events Cello tracks.

**Do this:** log in as a new user *without* opening the widget, so the component has to boot on its own. Open dev tools → Network, filter for `cello`, and reload. Then ask your coding tool for your integration status, and open the widget as a referrer.

**You should see**

| Where               | What good looks like                                                                                                             |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Dev tools → Network | A `v2?...` request appears, confirming Cello booted at login and not just loaded                                                 |
| Integration status  | **Referral component** is **Connected**, with no recent errors on the card                                                       |
| Your product        | The launcher and widget are positioned so they don't interfere with your UI, and don't clash with other pop-ups such as Intercom |
| Your product        | Copy and the reward or offer in the widget, on both the Invite tab and How-it-works, are accurate                                |
| Your product        | **Add payment details** shows the payout methods you intend referrers to use                                                     |
| Your product        | After you log out, the widget is no longer reachable                                                                             |
| Your product        | The widget works across browser sizes and on a mobile browser, not just desktop                                                  |

**If it's not working**

* If only `cello.js` loads and no `v2?...` request follows, Cello is loaded but not booted. Boot it on login.
* A **JWT token** error means the widget token is missing, malformed, or expired. Regenerate it on your backend - see [User Authentication](/sdk/client-side/user-authentication).

### Notifications

The badge, in-app announcements, and emails that keep referrers engaged. See [Referral notifications and emails](/guides/user-experience/referral-notifications-and-emails).

**Prerequisites**

* The referral component is integrated and booting, so the badge and announcements can anchor to the launcher. If you use a custom launcher, it's anchored to a unique selector.
* The boot command sends `productUserDetails` with at least `email`, and `firstName` for personalization. Both are required for emails - see the [boot command](/referral-component/quickstart).
* Notifications send on their own in pre-launch, so they reach your test users when the activity that triggers them happens.

**Do this:** review the email and announcement previews in the Portal, then run the activity behind each notification - have a referral link viewed, and let a referred signup and a reward come through - so the badge, announcement and email reach your test user. To check an announcement's placement without waiting for a trigger, render one on demand from your browser dev console with [`window.Cello("showAnnouncement", ...)`](/sdk/client-side/cello-js-usage#showannouncement-announcement), for example `{ type: "welcome-announcement-1" }`.

**You should see**

| What             | What good looks like                                                                                                                                                                                     |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Badge**        | Appears on the launcher when a link is viewed or a reward is earned, and on all menu items if you use a 2nd-level menu. Allow up to an hour - the badge follows the same refresh cycle as the dashboards |
| **Announcement** | Displays in full and is anchored in the correct position to the launcher                                                                                                                                 |
| **Email**        | The copy explains how to open the referral program in your product, the logo is sharp, the CTAs work, the reward amounts are correct, and you've signed off on the welcome email                         |

**If it's not working:** if the announcement doesn't show, check your [Announcement Selector](/referral-component/custom-launcher#troubleshooting-announcement-not-showing) matches exactly one visible element.

### Capturing the referral code

The attribution library (`cello-attribution.js`) on your landing pages, capturing the referral code (`ucc`) when someone clicks a referral link.

**Prerequisite:** the attribution library is embedded on every page reachable from a referral link, and your signup URL is set in **User Experience → Settings**. See [Web attribution setup](/attribution/for-web).

**Do this:** open one of your referral links in a fresh incognito window to land on your page, then ask your coding tool for your integration status.

**You should see**

| Where              | What good looks like                                                                                                                            |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Integration status | **Attribution library** is **Connected**                                                                                                        |
| Your landing page  | You land on the correct page - signup, website, or referral landing page                                                                        |
| Your landing page  | Cello's UCC is stored as a first-party cookie, under dev tools → Application → Cookies. This is what makes indirect signups attributable        |
| Your landing page  | The new-user banner shows with the right copy, offer, colors, and **referrer personalization** such as the referrer's first name, if configured |

**If it's not working:** "no signup URL" means you need to set your signup URL in **User Experience → Settings**. If it stays **Not connected**, confirm `cello-attribution.js` is embedded on the landing pages reachable from your referral links.

### Signup tracking

Referred signups reaching Cello and being attributed to the referrer.

**Prerequisite:** your **signup source** is set (self-serve in pre-launch) and signups are implemented for that source.

<Note>
  **You can set the signup source yourself, and change it while you're still testing.** The key Stripe/Chargebee vs Cello API rule: Stripe/Chargebee can only be the signup source if your payment-provider customer is created **at signup**. IF the customer is created **at first purchase**, use the Cello API to send `new-signup` at the real signup moment instead. The right choice also depends on your payment provider, reward model, and motion - see [Changing the configured source](/mcp/developers/integration-status#changing-the-configured-source) for all the factors. If you're unsure which source fits your setup, confirm it with Cello before you build against it.
</Note>

<Tabs>
  <Tab title="Cello API">
    Source: **Cello API** (`new-signup` events). You send events yourself via [`POST /events`](/api-reference/generic-events/send-event). See [Track Signups](/attribution/tracking-signups).

    Needs an API key under **Developers → Access keys** - generate one if there isn't one.
  </Tab>

  <Tab title="Stripe">
    Source: **Stripe** (`customer.created`, `customer.updated`). Events flow via the [Stripe webhook](/integrations/webhooks/stripe-webhook).

    Needs the Stripe webhook installed and pointed at the correct Cello endpoint.
  </Tab>

  <Tab title="Chargebee">
    Source: **Chargebee** (`Customer Created`, `Customer Changed`). Events flow via the [Chargebee webhook](/integrations/webhooks/chargebee-webhook).

    Needs the Chargebee webhook configured and forwarding events to Cello.
  </Tab>

  <Tab title="Auto Attribution">
    Source: **Auto Attribution** - Cello infers signups without you sending events. See the [Auto Attribution guide](/guides/attribution/auto-attribution).

    Needs Auto Attribution enabled for your tenant - contact Support if it's not in the dropdown.
  </Tab>
</Tabs>

**Do this:** complete a referred signup by following a referral link and signing up, then check your integration status and open **Developers → Events**. Repeat for **every signup method you support**, testing both a **direct** signup (open the link, go straight to signup) and an **indirect** one (open the link, browse around your site, then sign up).

**You should see**

| Where               | What good looks like                                                                                                                                              |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Integration status  | **Signups** is **Connected**, with a recent **last attributed signup**                                                                                            |
| Developers → Events | The signup event is **OK**, with no red **Required** or **Invalid** flags on `ucc` or `newUserId` / `productUserId`                                               |
| Your product        | The **direct** signup is attributed to the referrer                                                                                                               |
| Your product        | The **indirect** signup is attributed to the referrer. This one relies on the attribution cookie from [Capturing the referral code](#capturing-the-referral-code) |

**If it's not working:** "events received, none attributed" means your events are arriving without a referral code. Open **Developers → Events** to see which `ucc` is missing or malformed.

<Note>
  When you change a source, that component reverts to **Not connected** until events from the new source start arriving, usually within \~10 minutes. Only events that include a referral code (`ucc`) appear in **Events**.
</Note>

### Applying discounts

The new-user discount your referred users get at checkout. Applies if you offer a discount, typically freemium or trial.

**Prerequisite:** a new-user discount is configured for your campaign. See [Apply discounts](/attribution/apply-discounts) and [New user discounts](/guides/user-experience/new-user-discounts).

**Do this:** as the referred user from [Signup tracking](#signup-tracking), go to your upgrade or checkout page.

**You should see**

| Where               | What good looks like                                                                                                                    |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Your checkout       | The discount is visible **before** entering card details, ideally on the pricing page                                                   |
| Your checkout       | The discount math is correct for **monthly** and **yearly** plans                                                                       |
| Your checkout       | The reward to the referrer for the first month does **not** exceed what the new user pays in the first month. This is a fraud safeguard |
| Developers → Events | The discounted amount comes through on the purchase event - confirm it as part of [Purchase tracking](#purchase-tracking) below         |

### Purchase tracking

Referred purchases reaching Cello with the right fields, so rewards can be calculated.

**Prerequisite:** your **purchase source** is set (self-serve in pre-launch) and purchases are implemented for that source.

<Note>
  **You can set the purchase source yourself, and change it while you're still testing.** The correct source depends on your payment provider and reward model, and the wrong source silently breaks attribution rather than failing loudly - so if you're unsure which one fits, confirm it with Cello before you build against it. See [Changing the configured source](/mcp/developers/integration-status#changing-the-configured-source) for all the factors.
</Note>

<Tabs>
  <Tab title="Cello API">
    Source: **Cello API** (`invoice-paid` events). See [Track Purchases](/attribution/tracking-purchase).

    Required fields: `ucc`, one of `newUserId` / `productUserId`, plus `invoiceId`, `interval`, and `currency`. `price` is validated when present, and the timestamp must be a valid Unix timestamp.
  </Tab>

  <Tab title="Stripe">
    Source: **Stripe** (purchase events such as `invoice.paid` and `charge.succeeded`) via the [Stripe webhook](/integrations/webhooks/stripe-webhook).

    Required fields: `ucc` and `newUserId`. `email` is recommended, and required if you use Auto Attribution.
  </Tab>

  <Tab title="Chargebee">
    Source: **Chargebee** (purchase events) via the [Chargebee webhook](/integrations/webhooks/chargebee-webhook).

    Required fields: `ucc` and `newUserId`. `email` is recommended, and required if you use Auto Attribution.
  </Tab>
</Tabs>

**Do this:** with the referred user from [Signup tracking](#signup-tracking), complete a test purchase using your gateway's test card, then check your integration status and open **Developers → Events**.

**You should see**

| Where               | What good looks like                                                                                                    |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Integration status  | **Purchases** is **Connected**, with a recent **last attributed purchase**                                              |
| Developers → Events | The purchase event is **OK**, with every required field valid                                                           |
| Developers → Events | `email` is present. Yellow **Recommended** warnings are fine unless you use Auto Attribution, where `email` is required |

## Alternative scenarios

Only work through the ones that describe your setup.

### Partners only

You run a partner or affiliate program with no in-app referral widget. Partners get their sharing link and see their performance in the Cello-hosted [Partner Portal](/guides/partners/partner-portal), so `cello.js` is never deployed in your product.

**What still applies:** [Capturing the referral code](#capturing-the-referral-code), [Signup tracking](#signup-tracking) and [Purchase tracking](#purchase-tracking), exactly as written above. A partner's referral link is attributed the same way any referral link is.

**What doesn't apply:** [Referral component](#referral-component) and [Notifications](#notifications). Both test the in-app widget, which you don't have.

<Note>
  Expect **Referral component** to read **Not connected** in your integration status. The health check reports all four components every time, and with no widget deployed there are no boot events for it to see. That is correct for a partners-only setup and doesn't block your launch.
</Note>

**Prerequisites**

* A **partner campaign** exists. You can't invite partners without one.
* You've invited yourself as a test partner from **Partner Users**. See [Manage partners](/guides/partners/manage-partners).

**Do this:** register from the invite email, then run a full referral through your own partner link - land on your site, sign up as a new user, and complete a test purchase.

**You should see**

| Where                | What good looks like                                                                                                          |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Partner invite email | Arrives, and the registration link works                                                                                      |
| Partner Portal       | Your sharing link, the offer, and the reward details are all correct                                                          |
| Partner Portal       | Payout setup is reachable and offers the methods you intend partners to use                                                   |
| Your landing page    | The partner link lands on the right page and sets the UCC cookie                                                              |
| Developers → Events  | The signup and the purchase from the partner link both validate as **OK**                                                     |
| Partner Portal       | The test signup and purchase appear under the partner's performance. Allow an hour - this follows the dashboard refresh cycle |
| Partner Portal       | Any [partner resources](/guides/partners/partner-resources) you uploaded are visible to the partner                           |

For how the program is set up end to end, see the [Partner program overview](/guides/partners/partner-overview).

### Mobile app

Users can sign up directly inside your mobile app, so attribution has to work there too. See the [Mobile SDKs](/sdk/mobile/introduction).

| What               | What good looks like                                                                                       |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| Referral component | Opens, loads correct copy and rewards, and closes properly. Test iOS, Android, and React Native separately |
| Signup             | A signup from the app is attributed to the referrer and shows **OK** in **Events**                         |
| Purchase           | A purchase from the app is attributed and shows **OK** in **Events**                                       |

### Organization-level attribution

Rewards attach to an organization or team rather than an individual user.

| What                | What good looks like                                                                                                                                   |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| UCC scope           | Two users in the same org see the **same** UCC; users in different orgs see **different** UCCs                                                         |
| Event payload       | Signup and purchase events carry the org id (`organizationId` / `payload.newUserId`) and the user id (`context.newUser.id`) as defined for your source |
| Developers → Events | `organizationId` validates **OK** and is **not equal** to `newUserId`                                                                                  |

### Auto Attribution

You opted into Auto Attribution instead of sending your own events. See the [Auto Attribution guide](/guides/attribution/auto-attribution).

| What                | What good looks like                                                                                  |
| ------------------- | ----------------------------------------------------------------------------------------------------- |
| Widget              | After a referred signup, the widget shows the signup \~10-20 seconds later                            |
| Developers → Events | The signup appears tagged as Auto Attribution. It won't appear in real time the way webhook events do |
| Signups data        | \~5-10 minutes later, the signup shows with source **AA**                                             |

### Report-based attribution

Only if you can't send events automatically. This is less reliable and higher-maintenance - prefer a webhook or the Cello API where possible. See [Report-based attribution](/guides/attribution/manual-attribution).

## Launch your program

When the checks above pass, you're ready to go live. Do one last thing to your payment provider first, then launch from the **Home** page.

### Switch your payment provider to live credentials

You tested against your gateway's test mode, so the webhook forwarding events to Cello is a test-mode endpoint with a test-mode signing secret. Stripe and Chargebee keep test and live as separate environments, so that webhook never fires for a real purchase - signups and purchases would silently stop being attributed the moment you go live.

Add the same webhook in live mode, update the signing secret under **Developers → Webhooks**, and re-check your integration status once a real event has come through.

This is about your payment provider's environment, not Cello's. Your Cello account is already production - launching only takes it out of pre-launch mode.

### Confirm the pre-launch checklist

On the **Home** page, open the **Go live** card. A short **pre-launch checklist** appears confirming the essentials this testbook covers - one item per area above:

* Referral widget loads and opens for signed-in users
* Notifications work: badge, announcement, email
* New users land on the correct page from a referral link
* Signups are attributed to the referrer
* The new-user discount shows correctly at checkout, if your offer includes one
* Purchases are attributed to the referrer

The four event-based items also report **Connected** in your integration status. Confirm them, then **Launch program**.

<Tip>
  **Check your recommendations before you launch.** The card next to **Go live** scores a set of optional [performance enhancements](/integration-overview#performance-enhancements). A low score never blocks **Launch program**, but it is the best early signal of how the program will perform once you're live - and the ones that need code are much cheaper to fold in now, while you're still in pre-launch.
</Tip>

<Warning>
  Launching **can't be undone**. It takes your account out of pre-launch (testing) mode, so everything moves to the **Live** column of the [table above](#integrate-test-and-launch): rewards pay out, notifications reach real referrers, fraud checks run, and the payout delay applies. To test changes after launch, use a sandbox.
</Warning>

### After you launch

Re-check your integration status periodically. It flags when an upstream change - a redeployed script, a rotated webhook secret - silently breaks a component.
