A Bubble app can collect emails in minutes, but the default route is a Bubble database thing created by a button workflow. That gives you a list of addresses and nothing else: no referral code per signup, no record of which link brought each person, no ranking of who is bringing friends.

The fix is two small pieces: one script in Bubble's SEO settings and one HTML element where the form should sit. For a form built from native Bubble inputs, a third piece is the Toolbox plugin, which lets a workflow run one line of JavaScript.

Paste the OperatorStack script tag into Settings > SEO / metatags > Script/meta tags in header, then drop an HTML element containing <div data-os-widget="waitlist"></div> on the page. For a form that uses Bubble inputs, install the Toolbox plugin and call OperatorStack.joinWaitlist() from a Run javascript action.

What You Need Before You Start

  • A Bubble app you can edit.
  • An OperatorStack account. Free to start. You need the pk_ project key from your embed snippet.
  • The Toolbox plugin, only for the custom form route below.

Step 1: Add the Script to the Header

1

Create an OperatorStack project and copy your snippet:

<script src="https://operatorstack.dev/os.js" data-project="pk_abc123"></script>
2

In the Bubble editor, open Settings > SEO / metatags. Paste the snippet into Script/meta tags in header.

3

Open the app with Preview, load any page, and check the Network tab for a request to os.js. Seeing it on a page that has no form confirms the tag is app-wide.

Because the tag loads on every page, you also get cookie-free analytics across the whole app, with no consent banner.

Step 2: Place the Form With an HTML Element

4

Add a Visual element > HTML where the form belongs and paste:

<div data-os-widget="waitlist"></div>

Do not paste the script tag here. The header field already loads os.js.

Only the first data-os-widget="waitlist" div on a page renders. A second one on the same page stays empty by design, so pick one placement.

The design canvas often shows the HTML element as empty. Use Preview and look at the running page before you assume the install failed.

A Form Built From Bubble Inputs

If the widget style does not fit your app, build the form with a Bubble Input and a Button, then install the Toolbox plugin. In the button's workflow, add the Toolbox action Run javascript and paste:

OperatorStack.joinWaitlist({ email: properties.param1 }).then((res) => {
  bubble_fn_referral(res.referral_code);
});

Set the action's first property field to the Input's value, which Toolbox exposes to the script as properties.param1. To get the referral code back into Bubble, add a Toolbox JavaScript to Bubble element named referral: its bubble_fn_referral function publishes the value for use in a workflow or custom state.

joinWaitlist() resolves with { success, referral_code, referral_link }. To turn the code into share URLs for a thank-you group, call OperatorStack.getShareLinks(referral_code, { text: "I just joined the waitlist" }), which returns { twitter, linkedin, email, whatsapp, copy }.

getShareLinks() builds the URL from the current origin and path, so a form on yourapp.bubbleapps.io/launch produces links ending /launch?ref=CODE. To point somewhere else, build the URL yourself from referral_code.

Common Mistakes

  • Pasting the script into both the header and the HTML element. The page loads os.js twice. Script in settings, div in the element.
  • Judging the install from the design canvas. Check Preview or the live URL.
  • Two widget divs on one page. Only the first renders.
  • Running the JavaScript action before the email is validated. Add an Input's value is not empty condition on the workflow so blank submissions never reach the SDK.

Frequently Asked Questions

Where do I paste a script tag in Bubble?

In the editor, open Settings > SEO / metatags and use the Script/meta tags in header field. Anything there loads on every page of the app. Use the Body field only if a script needs to run after the page markup exists.

Do I need a plugin to add a waitlist to Bubble?

No, not for the standard widget. A script tag in the settings plus an HTML element containing the widget div is enough. You only need a plugin, such as Toolbox, when you want to build the form from native Bubble inputs and call the SDK from a workflow.

Why is the form empty inside the Bubble editor?

Third-party scripts often do not render in the design canvas. Open the app with Preview, or the live URL, and check the form there before debugging.

Can I store signups in the Bubble database too?

Yes, but you do not need to. OperatorStack keeps the contact, source and referral count in its own contact list. If you also want a Bubble record, add a Create a new thing step before the JavaScript action in the same workflow.

Does the referral code survive moving between Bubble pages?

A visitor who arrives with ?ref=CODE has it stored in sessionStorage under os_ref for that browser tab, so it survives page changes within the visit. It does not survive a closed tab.