Portage — Product Spec for Development

Portage.
by Northern Stay

This document is the product spec for building Portage — the direct booking infrastructure layer for Northern Stay. It covers brand, tiers, fee logic, user types, lead flows, every live page, and every integration still needed.

Identity specs for both brands
Northern Stay — Membership Brand
#1a3d2a
#c8893a
#f4f1ec
Cormorant Garamond 700
Cormorant Garamond italic
Inter — body and UI
Portage — Booking Infrastructure Brand
Portage.
by Northern Stay
#c2410c
#c8893a
#1a3d2a
Inter 800 — wordmark
Cormorant Garamond — "by Northern Stay"
Inter — body and UI

Portage-facing screens (operator dashboard, guest checkout, white-label sites) use the Portage mark. NS member-facing screens use the Northern Stay mark. The wordmark is always Inter 800, tight tracking. The period is always #c2410c. The "by Northern Stay" sub-label is always Cormorant Garamond italic.

Portage Promise

"You see every review before it goes live. You have 72 hours to reach out. If there's a dispute, it stays hidden while we talk to both sides."


What each plan includes
Free
$0/mo
No monthly fee. Guest pays 5.9% service fee. Operator pays Stripe processing on payout.
NS-hosted campground page at /campground/[slug]
Booking inquiry form — confirmed bookings land in operator inbox
Schema markup, photos, amenity data, SEO indexing
Direct online booking — guest pays at time of booking
Stripe Connect payout — direct deposit to operator bank account
Availability calendar
Site naming and hookup configuration
Early check-in / late check-out rules
Review management (72hr operator dispute window)
Booking reports
No dynamic pricing
NS branding visible to guests
Operator manages all guest communication
Managed
$299/mo
Month-to-month. No contract. Cancel anytime.
Everything in Pro
White-label booking website under operator's own brand and domain
NS invisible to guests — operator's logo, colours, name
NS handles all guest communication end-to-end
Booking admin handled by Northern Stay team
Custom colour scheme loaded from operator config
Portage payment rails and infrastructure underneath

Add-ons shown at checkout — fully configurable per operator

At the end of the guest checkout flow, a popup presents optional upgrades. Operators configure which add-ons they offer, the price, and the description from their dashboard. The list below is a default template — each operator's actual offerings will differ. Add-on revenue goes directly to the operator; NS does not take a cut beyond normal Stripe processing.

Default add-on categories (operator customises per property)

FW
Firewood Bundle
Pre-cut hardwood delivered to site on arrival. Quantity and price set by operator.
Operator sets price + qty
EX
Guided Excursion
Kayak tour, hiking guide, whale watching, etc. Operator defines excursion type, capacity, and available slots.
Operator sets name, slots, price
EI
Early Check-In / Late Check-Out
Guest pays to guarantee a specific arrival/departure time outside default windows.
Operator sets availability + price
KT
Welcome Kit
S'mores pack, local snacks, trail map bundle, or any physical package operator wants to offer.
Operator defines contents + price
EQ
Equipment Rental
Kayaks, paddleboards, bikes, lawn chairs, portable fire pits — any rentable gear the operator has on property.
Operator sets inventory + daily rate
+
Custom Add-On
Operators can create any add-on from their dashboard — name, description, price, per-night or per-stay, max qty per booking.
Fully operator-defined
Almost done — add extras to your stay
x
Murphy's Camping on the Ocean
Add-ons for your stay
Jul 18–21 · 3 nights · 2 guests
Firewood Bundle — 2 nights
Hardwood delivered to your site on arrival
$28
Kayak Rental — per day
Single kayak, paddle + PFD included
$45/day
Early Check-In (noon)
Guaranteed 12pm arrival vs standard 3pm
$20
Welcome S'mores Kit
At your site when you arrive
$14
Booking total $237.47
Nightly rate $65 × 3 + 5.9% service fee + firewood $28
Skip add-ons and pay $209.47
Add-ons charged at booking. Operator manages availability and fulfilment.

Implementation notes: Add-ons are configured per-operator in the dashboard. Each add-on has: name, description, price, per-night vs per-stay toggle, max qty per booking, and an active/inactive toggle. The checkout popup is triggered after Stripe payment details are entered but before final confirmation — same timing pattern as Airbnb's add-ons flow. Add-on line items appear in the booking summary email to the guest and in the operator booking notification.


Calendar, bookings, and property management — built to match what operators already know

The operator dashboard is the primary surface campground owners interact with. Structure and conventions should follow Airbnb host dashboard, Campspot, and Lodgify — operators coming from these platforms should feel at home immediately. The calendar view is the centrepiece.

Portage. Murphy's Camping on the Ocean
Calendar Bookings Pricing Reviews Payouts Add-ons Settings
July 2026
Month Week Day
Available Booked Pending Blocked
Site
Mon 14
Tue 15
Wed 16
Thu 17
Fri 18
Sat 19
Sun 20
Site 1
Full hookup · 50A
Booked
→ Checkout
Open
Open
Booked
Site 2
Water + 30A
Open
Pending
Site 3
Tent / no hookup
Blocked
Open
Booked
→ Checkout
Cabin A
Glamping unit
Booked
Booked
Open
Click any open date to block or set pricing. Click a booking to view guest details.
+ Block dates Export calendar

Calendar requirements:

Reference implementations: Airbnb host calendar (multi-listing row view), Campspot/Newbook operator portal (site-grid view). This is the screen operators will live in daily — it needs to load fast, work on mobile, and make conflicts obvious at a glance.


How money moves
$0
Host Fee
Operators pay nothing to list or receive bookings.
5.9%
Guest Service Fee
Added to nightly rate at checkout. Paid by guest.
~2.9%
Operator Pays — CC Processing
+ $0.30 per transaction. Deducted by Stripe when money lands in operator's bank account. Same as any card payment they take today. NS does not absorb this — it is the operator's Stripe processing cost.

How the money actually moves: Guest pays nightly rate + 5.9% service fee at checkout. NS collects the full amount via Stripe. NS pays out the full nightly rate to the operator via Stripe Connect. Stripe deducts ~2.9% + $0.30 from that payout before it hits the operator's bank. NS retains the 5.9% service fee as revenue.

The fee calculator on screen 3 of the intake wizard shows this live. Operator enters a nightly rate, sees the guest-facing total and their own net payout after processing. This is the key conversion screen — operators make their decision here.

PlatformHost FeeGuest FeeNet to Host
Portage. $0 5.9% (guest pays) Full nightly rate minus CC processing
Airbnb 15.5% Removed (built in) ~84.5% of nightly rate
Hipcamp ~15% Varies ~85% of nightly rate
Campspot 10%+ Quote only Varies

Sources: Airbnb Resource Centre Oct 2025 · Hipcamp Help Centre · Campspot Marketplace docs


Four distinct users. Each needs different permissions and flows.
Auth required
NS Member
  • Authenticated via Vercel auth
  • $0/night at 80+ partner campgrounds
  • Getaway: 30 nights/season, 60-day booking window
  • Lifestyle: up to 6 months/year, 90-day window
  • Wallet shows active membership tier
  • Booking history and upcoming stays
No auth required
Portage Guest
  • Unauthenticated
  • Pays full nightly rate + 5.9% service fee
  • No membership required to book
  • Gets confirmation email + booking summary
  • Sees membership pitch on confirmation page
  • Guest profile optionally created post-booking
Operator dashboard
Campground Operator
  • Authenticated — separate from member auth
  • Manages availability calendar
  • Sets nightly rates and dynamic pricing
  • Views and manages bookings
  • Moderates reviews (72hr dispute window)
  • Views payout reports
  • Configures site names, hookups, early check-in/out rules
  • Receives booking notifications
White-label tier
White-Label Operator
  • Everything Campground Operator has
  • Own domain, own branding
  • Portage infrastructure hidden from guests
  • Custom colour scheme loaded from operator config

Where every lead enters, what triggers, where it lands
Lead TypeEntry PointTriggerDestination
Membership lead Any SEO page CTA Click on Get Started /get-started quiz
Quiz lead /get-started Email submitted on quiz email screen /api/quiz-lead → Sheets quiz-leads tab → Julia drip email 1
Membership purchase https://northernstay.com/join/getaway-pass Stripe payment_intent.succeeded Worker confirmation email → Vercel must activate member account → member to /login
Checkout abandon https://northernstay.com/join/getaway-pass Email entered + tab closed before purchase /api/checkout-abandon → Sheets checkout-abandons → Julia recover email
Portage operator /campground-intake or /portage-claim Intake form submitted /api/portage-intake → Sheets portage-leads → operator confirmation email + admin alert
Guest booking inquiry Campground partner page Inquiry form submitted /api/book-inquiry → email to guest + campground → /booking-sent confirmation
Gas calculator lead /gas-calculator-canada Email submitted after calculation /api/calculator-lead → Sheets calc-leads tab → Julia email
Post-stay review Triggered by Angel manually /api/send-review-request called with booking data Review request email to guest → guest submits → 72hr operator dispute window → published or disputed

All Worker API endpoints post to Google Sheets via postToSheets(). All member emails are sent via SendGrid, signed by Julia. The Stripe webhook at /api/stripe-webhook is built on the Worker side — needs STRIPE_WEBHOOK_SECRET in Cloudflare env and a corresponding trigger on the Vercel side to activate the member account.


Every live page, its URL, and its role in the funnel
URLTagPurpose
northernstay.com/get-started NS Funnel 3-question membership quiz. Recommends Getaway Pass or Lifestyle. Captures email. Result screen shows campground photos for chosen region. ?tier=getaway and ?tier=lifestyle skip quiz and land directly on result.
northernstay.comhttps://northernstay.com/join/getaway-pass NS Funnel Membership checkout. Stripe card + Affirm (pending) + internal financing. Postal code tax detection. Consent-gated GA4/Meta/Pinterest/Reddit pixels. Abandon cart recovery on tab close.
northernstay.com/portage Portage Operator-facing landing page. Top of funnel for campground owners. Brand and offer overview. CTA routes to intake wizard.
northernstay.com/campground-intake Portage 7-screen operator onboarding wizard. Screen 3: live fee calculator. Screen 4: competitor table. Screen 5: dispute and chargeback coverage. ?park=slug pre-fills property name and shows existing NS listing link on screen 1.
northernstay.com/portage-claim Portage Alternate entry for operators who want to set up direct online booking. Routes to same intake flow.
northernstay.com/campground/[slug] NS Partner 80 verified partner pages. Full amenity data, photos, booking inquiry form, membership CTA, schema markup. Bottom of every page: operator claim banner routing to /campground-intake?ref=owner-page&park=slug.
northernstay.com/booking-sent NS Funnel Booking inquiry confirmation. Shows booking summary, unconfirmed status, next steps. Membership pitch at bottom.
northernstay.com/[province]-campgrounds SEO Provincial directory pages. Rank for "[province] campgrounds" queries. List partner + directory campgrounds. Drive organic traffic into membership funnel.
northernstay.com/gas-calculator-canada Tool Gas cost calculator. Ranking ~position 8 nationally. Email capture at end routes to /api/calculator-lead and Julia drip.
northernstay.com/great-northern-route Tool Halifax to Alaska interactive map. 75 NS campgrounds plotted. Province-by-province route with gas cost estimates per driving leg. Drives membership intent for long-haul campers.

What data is collected and what fires on submit

Every form submission routes through the Worker. Fields below are what get posted, stored in KV, pushed to Sheets, and used to populate emails.

Guest Booking Inquiry — campground partner pages Built · POST /api/book-inquiry

Form fields collected: first_name, email, phone, arrival, departure, guests, rig_type, rig_length, slide_outs, site_type, marketing_consent

On submit fires:

1. Guest confirmation email — from bookings@northernstay.com · Subject: "Inquiry sent to [Park Name]"
Contains: warm opener, "dates NOT confirmed" warning in amber, direct call/email buttons to campground, booking summary table (park, dates, guests, rig), membership pitch in forest green footer.

2. Campground notification email (partner pages) — from bookings@northernstay.com · Subject: "[Booking Referral] [Park] — [Date] — Full rate, paying guest"
Contains: Portage. header, "not a member booking" notice in amber, guest contact card with reply + call buttons, trip details table (arrival, departure, nights, guests, rig, site type), calendar block links (Google, Outlook, .ics download).

3. If marketing_consent = yes: schedules marketing drip sequence via scheduleMarketingDrip()

Portage Operator Intake — /campground-intake wizard Built · POST /api/portage-intake

Form fields collected: property_name, property_type, owner_name, province, email, phone, website, num_sites, tier_interest, current_booking, notes, contract_signed, contract_version, agreed_marketing, agreed_listing, agreed_bookings, agreed_response, agreed_terms

On submit fires:

1. Admin alert to admin@northernstay.com — all fields as a data summary table. Subject: "[Portage Application] [Property Name] — [Province]"

2. Operator confirmation email to owner — Subject: "Your Portage application is in — here's what happens next"
Julia-voiced. Confirms receipt, outlines next steps (review, approval, account setup), sets expectation for timeline.

3. Stored in KV for 90 days under portage-intake/[slug]/[timestamp]

4. Pushed to Google Sheets portage tab with all fields + contract metadata

5. Portage drip record created in KV for follow-up sequence

Membership Quiz Lead — /get-started email screen Built · POST /api/quiz-lead

Form fields collected: email, first_name, province, marketing_consent, nights (A[1]: low/mid/high), pain (A[2]: avail/afford/friction/planning/newbie), travel (A[3]: bc/ab/on/qc/maritimes/all), recommended_plan (getaway/lifestyle)

On submit fires:

1. Pushed to Sheets quiz-leads tab

2. Julia drip email 1 — subject line varies by pain point:
  afford → "What $0/night actually looks like"
  avail → "How members get the spots everyone else misses"
  friction → "One login. Every park. No confusion."
  planning → "The simple reason members never stress about camping anymore."
  newbie → "Where to start when you don't know where to start"

3. If consent checked: sets localStorage ns_consent = 'all', fires Meta Lead, Pinterest Lead, Reddit Lead, GA4 generate_lead

Membership Checkout — https://northernstay.com/join/getaway-pass Partial · Stripe test mode · webhook needed

Data collected: email, first_name, province (from postal code FSA), tier (getaway/lifestyle), payment_method (card/affirm/financing), postal_code

On Stripe payment_intent.succeeded fires:

1. Member welcome email from Julia — Subject: "You're in. Welcome to Northern Stay." (Lifestyle) or "Your Getaway Pass is active." (Getaway). Contains 3 onboarding steps + CTA to /login.

2. Admin alert to admin@northernstay.com — name, email, phone, tier, province, amount, payment ref, yellow callout: "Next step: Add to Vercel booking system"

3. Pushed to Sheets purchases tab

4. Missing: Vercel-side webhook to activate member account. This is the gap — member has paid but cannot log in until Vercel creates their account. Needs STRIPE_WEBHOOK_SECRET in Cloudflare env + corresponding trigger on Vercel.

Abandon cart: if email entered + tab closed before purchase → /api/checkout-abandon → Julia subject: "[Name], your Northern Stay membership is still waiting" → CTA to /get-membership?tier=[tier]. Deduped by email, 24hr TTL.


What's built, what's wired, what still needs connecting
Stripe Webhook — Member Account Creation Needed
When a membership purchase completes at https://northernstay.com/join/getaway-pass, Stripe fires payment_intent.succeeded to /api/stripe-webhook on the Worker. The Worker handles the confirmation email. What's missing: a corresponding trigger on the Vercel side that activates the member account so the buyer can log in and book. Need your webhook endpoint and what the account creation payload expects.
Post-Purchase Onboarding Flow Needed
After a successful purchase, the checkout success screen sends the new member to /login. That currently lands on a cold login page. It needs to land on a proper onboarding sequence — welcome, what to do next, how to make their first booking. This is the highest-value moment in the entire funnel and it's currently a dead end.
Stripe Connect — Operator Payouts Needed
Portage guest bookings need Stripe Connect to route payouts to campground operators. Operator onboarding flow needs a Stripe Connect account creation step after intake approval. Direct deposit to operator bank account. NS retains processing fee.
Google Sheets — Lead Capture Tabs Partial
Worker posts leads to Google Sheets via postToSheets(). The following tabs need to exist in the sheet: quiz-leads, calc-leads, gas-leads, financing-applications, purchases, checkout-abandons, portage-leads. Confirm sheet ID with Erin.
SendGrid — Transactional Email Built
All member emails sent via SendGrid from the Worker, signed by Julia. Covers: quiz lead drip, checkout confirmation, abandon cart recovery, calculator lead, GNR route plan, review requests. Admin alerts go to admin@northernstay.com.
Tracking Pixels — Membership Funnel Built
GA4 (G-6JCD0MF6Y6), Meta (1068047612088726), Pinterest (2612635872573), Reddit (a2_iuzfkkkgrcs5) — all consent-gated via localStorage.getItem('ns_consent') === 'all' on both /get-started and https://northernstay.com/join/getaway-pass. Events: quiz_complete, generate_lead, begin_checkout, InitiateCheckout, Purchase. Not yet on the Vercel app side — purchase events from the Vercel booking flow are not tracked.
Resort Pages — Crawlability Needed
The /resort/[slug] pages on Vercel are client-side rendered. Googlebot sees a loading state. These pages need SSR or static generation to be indexable. This is blocking organic search traffic to individual campground pages on the Vercel app side.
CRM State Persistence Needed
The sales CRM resets to page one on browser refresh. State needs to persist via localStorage or URL params so the sales team doesn't lose context mid-call. Small fix, high daily impact.
White-Label Domain Deployment — Managed Tier Clarification needed

The Managed tier gives operators a white-label booking website under their own brand and domain (e.g. book.murphyscamping.com). Portage infrastructure runs underneath — NS invisible to guests. Operator's logo, colours, and name on the front end.

Questions for Mike:

  • Can the Vercel app be configured to serve from an operator-supplied custom domain with their own branding loaded from their operator config record?
  • What's the deployment mechanism — subdomain CNAME pointed at a Vercel project, a separate Vercel deployment per operator, or a single multi-tenant app that resolves brand config by domain?
  • If the answer is yes (it can be done), the operator dashboard should include a guided domain setup flow: step-by-step DNS instructions, a DNS check tool that confirms the CNAME is live, and a "publish to your domain" button that is disabled until DNS is confirmed.

If custom domain deployment is straightforward, it becomes a strong Managed tier selling point — the operator doesn't need to build or host anything, they just point their domain and it goes live. If it requires per-operator builds, flag the operational cost to Erin before pricing the Managed tier.

Questions about Northern Stay or ready to book?