SOP-WL-01: Pre-Launch Waitlist System¶
Document Status: PRODUCTION READY Version: 1.0 Last Updated: 2026-03-12 Owner: Operations Classification: Internal Reference
1. Overview¶
1.1 Purpose¶
Capture email signups from pre-launch visitors via nine touchpoints across the site and sync to Customer.io for launch activation.
1.2 Scope¶
This SOP covers the waitlist-signup Edge Function, waitlist_signups database table, Shopify theme integration, Customer.io segment, and launch activation plan.
1.3 Architecture¶
Three capture points feed a single Edge Function, which writes to both Supabase and Customer.io:
Browser Form (3 sources)
│
▼
waitlist-signup Edge Function
│
├──▶ (1) raw_ops.waitlist_signups — upsert
├──▶ (2) Customer.io PUT /identify — create/update profile
└──▶ (3) Customer.io POST /track — waitlist_signup event
1.4 Capture Points¶
| Source | Location | Trigger |
|---|---|---|
calculator |
calculator-checkout-modal.liquid (Step 1, pre-launch mode) |
Email entered in modal |
homepage_hero |
waitlist-hero-cta.liquid (pre-launch mode) |
Email entered below hero |
homepage_mid |
waitlist-mid-cta.liquid (pre-launch mode) |
Email entered after Proof section |
homepage_footer |
final-cta.liquid (pre-launch mode) |
Email entered in founder statement CTA |
journal |
article-cta.liquid (pre-launch mode) |
Email entered on journal article |
ingredients |
ingredients-page.liquid CTA section (pre-launch mode) |
Email entered on Ingredients page |
nutrition |
nutrition-page.liquid CTA section (pre-launch mode) |
Email entered on Nutrition page |
how_it_works |
how-it-works-page.liquid CTA section (pre-launch mode) |
Email entered on How It Works page |
about |
about-page.liquid CTA section (pre-launch mode) |
Email entered on About page |
2. Database Schema¶
2.1 Table: raw_ops.waitlist_signups¶
CREATE TABLE raw_ops.waitlist_signups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL,
postcode TEXT,
postcode_area TEXT,
dog_name TEXT,
box_size TEXT,
daily_grams NUMERIC,
delivery_weeks INTEGER,
pet_count INTEGER,
dog_weight_kg NUMERIC,
weekly_price NUMERIC,
source TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
confirmation_sent_at TIMESTAMPTZ,
notified_at TIMESTAMPTZ,
converted_at TIMESTAMPTZ
);
-- Unique constraint on email (upsert target)
CREATE UNIQUE INDEX idx_waitlist_signups_email
ON raw_ops.waitlist_signups(email);
2.2 View: raw_ops.v_waitlist_stats¶
Analytics view for signup tracking.
-- Signup counts by source
SELECT source, COUNT(*) as signups
FROM raw_ops.waitlist_signups
GROUP BY source;
-- Daily signup trend
SELECT DATE(created_at) as date, COUNT(*) as signups
FROM raw_ops.waitlist_signups
GROUP BY DATE(created_at)
ORDER BY date DESC;
3. Edge Function: waitlist-signup¶
3.1 Location¶
Supabase Edge Functions: supabase/functions/waitlist-signup/index.ts
3.2 Environment Variables¶
| Variable | Purpose | Note |
|---|---|---|
SUPABASE_URL |
Database connection | Auto-injected |
SUPABASE_SERVICE_ROLE_KEY |
Database auth | Auto-injected |
CUSTOMERIO_SITE_ID |
Customer.io identify auth | Must read inside serve() handler |
CUSTOMERIO_API_KEY |
Customer.io track auth | Must read inside serve() handler |
Important: All Deno.env.get() calls must be inside the serve() handler function, not at module scope. Supabase Edge Functions do not reliably expose env vars at module init time. This was the root cause of Customer.io silent failures during initial deployment.
3.3 Request Payload¶
{
"email": "required",
"postcode": "optional",
"dog_name": "optional",
"box_size": "optional",
"daily_grams": "optional",
"delivery_weeks": "optional",
"pet_count": "optional",
"dog_weight_kg": "optional",
"weekly_price": "optional",
"source": "optional (calculator | homepage | journal)"
}
3.4 Two Database Paths¶
| Condition | Path | Method |
|---|---|---|
| Postcode provided | RPC: fn_waitlist_signup() |
Extracts postcode area, upserts with area |
| No postcode | Direct upsert | Upserts on email with available fields |
3.5 Customer.io Integration¶
Step 1: Identify (PUT)
PUT https://track.customer.io/api/v1/customers/{email}
Authorization: Basic base64(SITE_ID:API_KEY)
{
"email": "{email}",
"waitlist": true,
"waitlist_source": "{source}",
"dog_name": "{dog_name}",
"box_size": "{box_size}",
"daily_grams": {daily_grams},
"weekly_price": {weekly_price},
"created_at": {unix_timestamp}
}
Step 2: Track Event (POST)
POST https://track.customer.io/api/v1/customers/{email}/events
Authorization: Basic base64(SITE_ID:API_KEY)
{
"name": "waitlist_signup",
"data": {
"source": "{source}",
"dog_name": "{dog_name}",
"box_size": "{box_size}",
"weekly_price": {weekly_price}
}
}
4. Shopify Theme Integration¶
4.1 Theme Setting¶
File: config/settings_schema.json
{
"name": "Pre-launch Mode",
"settings": [
{
"type": "checkbox",
"id": "prelaunch_mode",
"label": "Pre-launch mode",
"default": true,
"info": "When enabled, calculator modal captures waitlist signups instead of proceeding to checkout. Disable at launch to restore full purchase flow."
}
]
}
Activation: Also set "prelaunch_mode": true in config/settings_data.json (schema defaults don't apply to existing themes).
4.2 Render Points¶
| File | Behaviour (pre-launch ON) | Behaviour (pre-launch OFF) |
|---|---|---|
snippets/calculator-checkout-modal.liquid |
Step 1 captures waitlist signup, shows success screen with plan summary | Normal 4-step checkout flow |
sections/waitlist-hero-cta.liquid |
Renders waitlist form on Warm Linen bg (source: homepage_hero) |
Renders nothing |
sections/waitlist-mid-cta.liquid |
Renders waitlist form on dark Espresso bg (source: homepage_mid) |
Renders nothing |
sections/final-cta.liquid |
Renders waitlist-form snippet (source: homepage_footer) |
Renders "Calculate Your Plan" button |
sections/article-cta.liquid |
Renders waitlist-form snippet (source: journal) |
Renders calculator CTA (or throttle CTA) |
sections/ingredients-page.liquid |
CTA section renders waitlist form (source: ingredients) |
CTA chain: Nutrition (primary) + Calculator (secondary) |
sections/nutrition-page.liquid |
CTA section renders waitlist form (source: nutrition) |
CTA chain: How It Works (primary) + Batch report (secondary) |
sections/how-it-works-page.liquid |
CTA section renders waitlist form (source: how_it_works) |
Calculator CTA |
sections/about-page.liquid |
CTA section renders waitlist form (source: about) |
Calculator CTA |
4.3 Waitlist Form Snippet¶
File: snippets/waitlist-form.liquid
Reusable email capture form accepting a source parameter. Features:
- Inline CSS (Montserrat/Inter, Burnt Sienna #B85C3A, Cream)
- Inline JS with loading states, validation, success/error handling
- Calls waitlist-signup Edge Function
- Mobile responsive (stacks on <480px)
4.4 Dark Background Form Styling¶
File: snippets/waitlist-dark-styles.liquid
Shared CSS overrides for rendering the waitlist form on dark Espresso backgrounds. Applied by wrapping the form render in a <div class="wl-dark"> container. Hides the snippet's own heading, body text, and note (the parent section provides these). Overrides input background, border, text, and placeholder colours for legibility on dark backgrounds. Used by waitlist-mid-cta.liquid and the four interior page CTA overrides (Ingredients, Nutrition, How It Works, About).
5. Customer.io Integration¶
5.1 Profile Attributes¶
| Attribute | Type | Set By |
|---|---|---|
waitlist |
boolean | waitlist-signup Edge Function |
waitlist_source |
string | waitlist-signup Edge Function |
dog_name |
string | waitlist-signup Edge Function (if captured) |
box_size |
string | waitlist-signup Edge Function (if captured) |
daily_grams |
number | waitlist-signup Edge Function (if captured) |
weekly_price |
number | waitlist-signup Edge Function (if captured) |
5.2 Event¶
Name: waitlist_signup
Trigger: Each signup (deduplicated by Customer.io on email)
Data: source, dog_name, box_size, weekly_price
5.3 Launch Campaign¶
Campaign: launch_announcement
Segment: All profiles where waitlist = true
Trigger: Manual broadcast on launch day
| Variant | Condition | Content |
|---|---|---|
| A: Full plan | dog_name exists |
Personalised with dog name, box size, daily grams, price |
| B: Generic | dog_name is null |
General launch announcement with calculator link |
From: Protocol Raw <hello@protocolraw.co.uk>
6. Launch Activation Checklist¶
When Protocol Raw is ready to go live, follow these steps in order:
| # | Action | Where |
|---|---|---|
| 1 | Uncheck prelaunch_mode in theme settings |
Shopify Admin > Themes > Customize > Theme Settings > Pre-launch Mode |
| 2 | Push theme to live | shopify theme push --theme 192286327159 --allow-live |
| 3 | Remove proof portal root redirect | proof-page/index.ts — delete the 302 redirect block, restore servePortalHomepage() |
| 4 | Deploy proof-page Edge Function | supabase functions deploy proof-page --no-verify-jwt |
| 5 | Send launch_announcement campaign |
Customer.io > Campaigns > launch_announcement > Send to waitlist segment |
| 6 | Monitor waitlist conversion | Check v_waitlist_stats and Customer.io campaign metrics |
7. Monitoring¶
7.1 Pre-Launch¶
No automated monitoring required. Use the v_waitlist_stats view for ad hoc signup tracking:
-- Total signups by source
SELECT source, COUNT(*) as signups
FROM raw_ops.waitlist_signups
GROUP BY source
ORDER BY signups DESC;
-- Daily trend
SELECT DATE(created_at) as date, source, COUNT(*) as signups
FROM raw_ops.waitlist_signups
GROUP BY DATE(created_at), source
ORDER BY date DESC;
7.2 Post-Launch¶
Track conversion from waitlist to paying customer:
SELECT
COUNT(*) as total_waitlist,
COUNT(converted_at) as converted,
ROUND(100.0 * COUNT(converted_at) / COUNT(*), 1) as conversion_pct
FROM raw_ops.waitlist_signups;
8. Related Documents¶
| Document | Relationship |
|---|---|
| Calculator-to-Purchase System v4.1 | Modal pre-launch mode documentation |
| SOP-PROOF-01 v1.1 | Sample batch report and pre-launch redirect |
| Homepage Documentation v1.3 | Homepage waitlist CTA and link updates |
| SOP-JOURNAL-01 v1.1 | Journal article waitlist CTA |
| SOP-EMAIL-01 v1.1 | Waitlist segment and launch campaign |
9. Version History¶
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.1 | 2026-03-14 | Claude | Expanded capture points from 3 to 9. Added homepage hero/mid sections, interior page CTA overrides (Ingredients, Nutrition, How It Works, About). Documented .wl-dark shared dark-background styling. Updated render points table. |
| 1.0 | 2026-03-12 | Claude | Initial specification |
Document Owner: Protocol Raw Operations