Skip to content

Protocol Raw Customer Portal Documentation

Version: 2.6
Created: November 30, 2025
Last Updated: January 21, 2026
Status: โœ… Production Ready
Portal URL: https://my.protocolraw.co.uk


What's New in v2.6

Transition Guide Feature: - โœ… New TransitionView with personalised 10-day feeding schedule - โœ… First-visit detection and automatic redirect for new customers - โœ… Deep linking support via ?view=transition URL parameter - โœ… Mobile-responsive design with browser back button support - โœ… "Getting Started" card on HomeView (shown for first 3 orders) - โœ… Multi-dog support with per-dog calculated portions

Previous versions: - v2.5: Payment method update via Seal magic link - v2.4: Credit balance display - v2.3: Documentation consolidation - v2.2: Self-serve address editing (with Shopify + Seal sync) - v2.1: Tracking card with shipment status - v2.0: Initial elevated design release


Table of Contents

  1. Overview
  2. Architecture
  3. Authentication
  4. Landing Page
  5. Portal Features
  6. Transition Guide
  7. Credit Balance Display
  8. Payment Method Update
  9. Shipment Tracking
  10. Address Editing
  11. 48-Hour Delivery Lock System
  12. Email Notifications
  13. Database Schema
  14. API Reference
  15. Edge Functions
  16. Make.com Integration
  17. Deployment
  18. Troubleshooting
  19. Version History

Overview

Purpose

The Protocol Raw Customer Portal is a self-service interface that empowers customers to manage their subscription, view order history, track deliveries, and verify batch safetyโ€”all without contacting support. Built as part of the AI-native operations strategy, the portal reduces contact volume by enabling 90%+ self-serve resolution.

Design Philosophy

"Self-Serve First" - Every common customer need addressable without support contact - Proactive information reduces need to ask questions - Friction-free subscription management increases retention

Visual Identity - Protocol Raw elevated brand aesthetic - Warm, premium color palette (Old Lace, Forest Green, Terracotta) - Clean typography (Montserrat headings, Inter body) - Consistent with main website and Proof Portal

Key Features

Feature Status Version
Magic link authentication โœ… Built v2.0
View subscription status โœ… Built v2.0
View next delivery date โœ… Built v2.0
Skip next delivery โœ… Built v2.0
Reschedule delivery โœ… Built v2.0
Pause subscription โœ… Built v2.0
Resume subscription โœ… Built v2.0
Change box size โœ… Built v2.0
Change frequency โœ… Built v2.0
Update feeding plan โœ… Built v2.0
View order history โœ… Built v2.0
View batch proof โœ… Built v2.0
Cancel subscription โœ… Built v2.0
48-hour delivery lock โœ… Built v2.0
Multi-dog support โœ… Built v2.0
Request portal access โœ… Built v2.0
Track current delivery โœ… Built v2.1
Update shipping address โœ… Built v2.2
Credit balance display โœ… Built v2.4
Update payment method โœ… Built v2.5
Transition Guide โœ… Built v2.6
First-visit redirect โœ… Built v2.6
Deep linking (?view=) โœ… Built v2.6

Architecture

System Components

Customer โ†’ Landing Page โ†’ Magic Link Email โ†’ Authenticated Portal
                                                    โ†“
                                            Supabase (Database)
                                                    โ†“
                                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
                                    โ†“               โ†“               โ†“
                              Customer Data    Seal API        Customer.io
                              (Orders, Dogs)   (Subscription)  (Events)

Technology Stack

Layer Technology Purpose
Frontend React (Vite) Single-page application
Hosting Cloudflare Pages Static hosting + edge caching
Database Supabase PostgreSQL Customer data, orders, shipments
Auth Magic Link (Custom) Passwordless authentication
Subscriptions Seal Subscriptions Shopify subscription management
Email Customer.io Transactional emails
Styling Inline + CSS Variables Protocol Raw brand system
Edge Functions Supabase Edge Functions Server-side API calls (e.g., Seal)

File Structure

customer-portal/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ App.jsx           # Main application
โ”‚   โ”œโ”€โ”€ Portal.jsx        # Authenticated portal view
โ”‚   โ”œโ”€โ”€ api.js            # Supabase client + API functions
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”œโ”€โ”€ Card.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ Button.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ Modal.jsx
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ””โ”€โ”€ views/
โ”‚       โ”œโ”€โ”€ HomeView.jsx
โ”‚       โ”œโ”€โ”€ SubscriptionView.jsx
โ”‚       โ”œโ”€โ”€ OrderHistoryView.jsx
โ”‚       โ”œโ”€โ”€ FeedingPlanView.jsx
โ”‚       โ””โ”€โ”€ TransitionView.jsx    # NEW in v2.6
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ vite.config.js

URLs

Production: - Landing: https://my.protocolraw.co.uk - Portal: https://my.protocolraw.co.uk/?token=<magic_link_token> - Transition Guide (deep link): https://my.protocolraw.co.uk/?token=<token>&view=transition


Authentication

  1. Customer requests access (landing page or email link)
  2. System generates secure token (UUID)
  3. Token stored in raw_ops.portal_tokens with 7-day expiry
  4. Customer.io sends email with magic link
  5. Customer clicks link โ†’ token validated โ†’ session created
  6. Token marked as used (single-use)

Token Schema

CREATE TABLE raw_ops.portal_tokens (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID REFERENCES raw_ops.customers(id),
  token UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
  expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '7 days'),
  used_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Session Management

  • Token validated on each API call
  • Session persists via localStorage
  • Auto-logout after token expiry
  • Re-authentication via "Send new link" flow

Security

  • Tokens are single-use (marked used after first validation)
  • 7-day expiry window
  • No password storage
  • Rate limiting on token requests (5 per hour per email)

Landing Page

Purpose

Entry point for customers without active session. Provides portal access request form and explains available features.

Components

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  [Protocol Raw Logo]                                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                 โ”‚
โ”‚         Manage Your Subscription                                โ”‚
โ”‚                                                                 โ”‚
โ”‚  Enter your email to receive a secure login link.               โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย                       โ”‚
โ”‚  โ”‚  Email address                       โ”‚                       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ”‚                                                                 โ”‚
โ”‚  [Send Login Link]                                              โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€      โ”‚
โ”‚                                                                 โ”‚
โ”‚  What you can do in your portal:                                โ”‚
โ”‚  โ€ข View and manage your subscription                            โ”‚
โ”‚  โ€ข Track your current delivery                                  โ”‚
โ”‚  โ€ข Update your feeding plan                                     โ”‚
โ”‚  โ€ข View your order history                                      โ”‚
โ”‚  โ€ข Access batch verification reports                            โ”‚
โ”‚                                                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

States

  1. Initial: Email input form
  2. Loading: Submitting request
  3. Success: "Check your email" confirmation
  4. Error: Email not found / rate limited

Portal Features

Home View

The main dashboard showing subscription status, quick actions, and recent activity.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  Welcome back, [Name]                              [Log Out]    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚  โš รฏยธย PAYMENT WARNING (if payment_status !== 'healthy')    โ”‚  โ”‚
โ”‚  โ”‚  We had trouble processing your last payment.            โ”‚  โ”‚
โ”‚  โ”‚  [Update payment method]                                 โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚  GETTING STARTED (if total_orders <= 3)        รขโ€ ย v2.6    โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  Your 10-Day Transition Plan                              โ”‚  โ”‚
โ”‚  โ”‚  A personalised feeding schedule for a smooth switch.     โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  View transition guide โ†’                                  โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚  NEXT DELIVERY                                            โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  Tuesday, 28 January                    [12 days]         โ”‚  โ”‚
โ”‚  โ”‚  12kg Protocol Raw Complete                               โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  [Skip This Delivery]  [Reschedule]                       โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย              โ”‚
โ”‚  โ”‚  Quick Actions      โ”‚  โ”‚  Your Subscription  โ”‚              โ”‚
โ”‚  โ”‚                     โ”‚  โ”‚                     โ”‚              โ”‚
โ”‚  โ”‚  โ€ข Change box size  โ”‚  โ”‚  Status: Active     โ”‚              โ”‚
โ”‚  โ”‚  โ€ข Change frequency โ”‚  โ”‚  Box: 12kg          โ”‚              โ”‚
โ”‚  โ”‚  โ€ข Update address   โ”‚  โ”‚  Every: 4 weeks     โ”‚              โ”‚
โ”‚  โ”‚  โ€ข Pause sub        โ”‚  โ”‚                     โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚  Credit Balance                               ยฃ15         โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚ โš    Credit expiring soon. Active subscriptions use โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ”‚   it automatically.                                 โ”‚  โ”‚  โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  Applied automatically to your next renewal.              โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚  VERIFICATION STATS                                       โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  [5] Boxes Received    [5] Batches Verified               โ”‚  โ”‚
โ”‚  โ”‚                                                           โ”‚  โ”‚
โ”‚  โ”‚  Your last batch: PR-E769561D [View Report โ†’]             โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Subscription Management

Change Box Size - Options: 8kg (ยฃ89), 12kg (ยฃ109), 16kg (ยฃ129) - Confirmation modal with price change - Effective from next billing

Change Frequency - Options: Every 2, 3, 4, 5, 6 weeks - Feeding plan recalculation shown - Effective from next billing

Skip Delivery - One-click skip with confirmation - Shows next scheduled date after skip - Cannot skip if within 48-hour lock

Pause Subscription - Confirmation with counter-offer (discount) - Pause duration options: 2, 4, 6 weeks - Auto-resume after pause period

Resume Subscription - One-click resume - Next delivery date shown

Cancel Subscription - Multi-step flow with retention offers - Reason selection - Final confirmation - Post-cancellation survey

Order History View

Timeline of all orders with batch links.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  Order History                                                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                 โ”‚
โ”‚  รขโ€”ย 15 January 2026                                              โ”‚
โ”‚    Order #1234 ยท 12kg ยท Delivered                               โ”‚
โ”‚    Batch: PR-E769561D [View Proof โ†’]                            โ”‚
โ”‚                                                                 โ”‚
โ”‚  รขโ€”ย 18 December 2025                                             โ”‚
โ”‚    Order #1189 ยท 12kg ยท Delivered                               โ”‚
โ”‚    Batch: PR-A3B7C912 [View Proof โ†’]                            โ”‚
โ”‚                                                                 โ”‚
โ”‚  รขโ€”ย 20 November 2025                                             โ”‚
โ”‚    Order #1156 ยท 8kg ยท Delivered                                โ”‚
โ”‚    Batch: PR-F4D2E891 [View Proof โ†’]                            โ”‚
โ”‚                                                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Feeding Plan View

Update dog details and see recommended feeding amounts.

Editable Fields: - Dog name - Weight (kg) - Life stage (Puppy/Adult/Senior) - Activity level (Low/Moderate/High/Very High) - Body condition (Underweight/Ideal/Overweight)

Calculated Display: - Daily feeding amount (grams) - Weekly consumption - Box duration at current consumption

Multi-Dog Support: - Add/remove dogs - Household total consumption - Combined feeding plan


Transition Guide

Overview

The Transition Guide replaces printed transition instructions with a personalised, interactive portal experience. New customers are automatically directed to this view on their first portal visit.

Purpose: Provides new customers with a personalised transition guide showing exact gram amounts for their dog(s), calculated based on weight, life stage, and activity level.

First-Visit Detection

Database Field:

-- Added to raw_ops.customers table
portal_first_visited_at TIMESTAMPTZ DEFAULT NULL

Behaviour: 1. Customer authenticates via magic link 2. System checks if portal_first_visited_at is NULL 3. If NULL (first visit): - Set portal_first_visited_at to current timestamp - Redirect to TransitionView 4. If NOT NULL (returning visit): - Show HomeView as normal

Edge Function Update (portal-session):

// After successful authentication, check first visit
if (!customer.portal_first_visited_at) {
  // Update the timestamp
  await supabase
    .from('customers')
    .update({ portal_first_visited_at: new Date().toISOString() })
    .eq('id', customer.id);

  // Include redirect flag in response
  return { ...sessionData, isFirstVisit: true };
}

Deep Linking

The Transition Guide can be accessed directly via URL parameter:

https://my.protocolraw.co.uk?token=xxx&view=transition

Use Cases: - Insert card QR code links directly to transition guide - Customer.io emails can deep link to this view - Support agents can share direct link

Implementation:

// In Portal.jsx useEffect
useEffect(() => {
  const params = new URLSearchParams(window.location.search);
  const viewParam = params.get('view');

  if (viewParam === 'transition') {
    setView('transition');
  }
}, []);

TransitionView Components

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  รขโ€ ย Back                                                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                     โ”‚
โ”‚  GETTING STARTED                                                    โ”‚
โ”‚                                                                     โ”‚
โ”‚  Your 10-Day Transition Plan                                        โ”‚
โ”‚  A measured approach to switching your dog to raw feeding,          โ”‚
โ”‚  based on established veterinary best practice.                     โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย   โ”‚
โ”‚  โ”‚  "You'll find different opinions online about how quickly   โ”‚   โ”‚
โ”‚  โ”‚   to transition. Our approach gives your dog's digestive    โ”‚   โ”‚
โ”‚  โ”‚   system time to adapt..."                                  โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€     โ”‚
โ”‚           A measured transition. A confident start.                 โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€     โ”‚
โ”‚                                                                     โ”‚
โ”‚  [โ–ผ Who this guide is for]                                         โ”‚
โ”‚  [โ–ผ When to consult your vet first]                                โ”‚
โ”‚  [โ–ผ Safe handling]                                                 โ”‚
โ”‚                                                                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  THE SCHEDULE                           (Warm Linen background)    โ”‚
โ”‚                                                                     โ”‚
โ”‚  [Dog Name]'s Schedule                                             โ”‚
โ”‚  Calculated based on weight, life stage, and activity level.       โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย   โ”‚
โ”‚  โ”‚  [Avatar] Luna                           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย    โ”‚   โ”‚
โ”‚  โ”‚           Adult ยท 25kg ยท Moderate        โ”‚ Target dailyโ”‚    โ”‚   โ”‚
โ”‚  โ”‚                                          โ”‚    400g     โ”‚    โ”‚   โ”‚
โ”‚  โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย                                               โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Day 1โ€“2  โ”‚  100g Protocol Raw Complete                   โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  + 75% current food                      โ–‘โ–‘โ–‘โ–‘โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย                                               โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Day 3โ€“4  โ”‚  200g Protocol Raw Complete                   โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  + 50% current food                    โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย                                               โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Day 5โ€“6  โ”‚  300g Protocol Raw Complete                   โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  + 25% current food                  โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย                                               โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Day 7โ€“8  โ”‚  350g Protocol Raw Complete                   โ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  + 10% current food                โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย โœ“                                             โ”‚   โ”‚
โ”‚  โ”‚  โ”‚ Day 9โ€“10 โ”‚  400g Protocol Raw Complete          โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ”‚   โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  (100% transitioned)                          โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  ๐Ÿ’ก Puppy tip (if life_stage === 'puppy')                  โ”‚   โ”‚
โ”‚  โ”‚  Split daily portions into 3โ€“4 smaller meals...            โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  WHAT TO EXPECT                                                    โ”‚
โ”‚                                                                     โ”‚
โ”‚  Normal Changes During Transition                                   โ”‚
โ”‚  These are signs the transition is working, not problems to solve. โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย              โ”‚
โ”‚  โ”‚ โŠ• Stool changes      โ”‚  โ”‚ ๐Ÿ’ง Drinking less     โ”‚              โ”‚
โ”‚  โ”‚                       โ”‚  โ”‚                       โ”‚              โ”‚
โ”‚  โ”‚ Stools may become    โ”‚  โ”‚ Raw food is naturallyโ”‚              โ”‚
โ”‚  โ”‚ softer initially...  โ”‚  โ”‚ high in moisture...  โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย              โ”‚
โ”‚  โ”‚ ๐Ÿ”ต Digestive adjust  โ”‚  โ”‚ โœจ Coat improvement  โ”‚              โ”‚
โ”‚  โ”‚                       โ”‚  โ”‚                       โ”‚              โ”‚
โ”‚  โ”‚ Some dogs experience โ”‚  โ”‚ Temporary dandruff   โ”‚              โ”‚
โ”‚  โ”‚ occasional bile...   โ”‚  โ”‚ as body adjusts...   โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                                     โ”‚
โ”‚  [โ–ผ Extra support for sensitive stomachs]                          โ”‚
โ”‚                                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย   โ”‚
โ”‚  โ”‚ โš รฏยธย When to pause and call your vet                         โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚ Trust your instincts. Pause the transition if you notice:  โ”‚   โ”‚
โ”‚  โ”‚ โ€ข Diarrhoea lasting more than 48 hours                     โ”‚   โ”‚
โ”‚  โ”‚ โ€ข Persistent vomiting                                       โ”‚   โ”‚
โ”‚  โ”‚ โ€ข Refusal to eat for more than 24 hours                    โ”‚   โ”‚
โ”‚  โ”‚ โ€ข Lethargy or signs of discomfort                          โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย   โ”‚
โ”‚  โ”‚  [Forest Green background]                                  โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  โœ“ VERIFIED                                                 โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  Verify your batch                                          โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  Every pouch has a QR code linking to independent lab       โ”‚   โ”‚
โ”‚  โ”‚  results for your specific batch. Scan any pouch to see     โ”‚   โ”‚
โ”‚  โ”‚  the safety certificate.                                    โ”‚   โ”‚
โ”‚  โ”‚                                                             โ”‚   โ”‚
โ”‚  โ”‚  Welcome to Protocol Raw.                                   โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Transition Schedule Calculation

Constants:

const TRANSITION_SCHEDULE = [
  { days: '1โ€“2', percent: 25, currentPercent: 75 },
  { days: '3โ€“4', percent: 50, currentPercent: 50 },
  { days: '5โ€“6', percent: 75, currentPercent: 25 },
  { days: '7โ€“8', percent: 90, currentPercent: 10 },
  { days: '9โ€“10', percent: 100, currentPercent: 0 },
];

Gram Calculation:

function roundTo25(grams) {
  return Math.round(grams / 25) * 25;
}

function getScheduleForDog(dog) {
  const dailyGrams = dog.daily_grams || 400;
  return TRANSITION_SCHEDULE.map(phase => ({
    ...phase,
    grams: roundTo25(dailyGrams * (phase.percent / 100)),
  }));
}

Example Output (25kg adult dog, 400g daily):

Days Protocol Raw Complete Current Food
1โ€“2 100g 75%
3โ€“4 200g 50%
5โ€“6 300g 25%
7โ€“8 350g 10%
9โ€“10 400g 0% โœ“

Multi-Dog Support

If customer has multiple dogs, the TransitionView displays a separate schedule card for each dog:

{dogs.map((dog, index) => (
  <DogScheduleCard key={dog.id || index} dog={dog} />
))}

Each card shows: - Dog name and avatar - Life stage, weight, activity level - Target daily grams (calculated via RER/MER methodology) - Personalised transition schedule - Puppy tip (if applicable)

HomeView Integration

"Getting Started" Card:

Shown on HomeView when total_orders <= 3 (first 3 orders):

{totalOrders <= 3 && (
  <TransitionCard onClick={() => setView('transition')} />
)}

Card Design:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  GETTING STARTED                                                    โ”‚
โ”‚                                                                     โ”‚
โ”‚  Your 10-Day Transition Plan                                        โ”‚
โ”‚  A personalised feeding schedule for a smooth switch to raw.        โ”‚
โ”‚                                                                     โ”‚
โ”‚  View transition guide โ†’                                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Styling follows Visual Identity Guide v2.2: - Warm Linen background (#EBE8E3) - Section label in Burnt Sienna (#B85C3A) - Green accent bar on hover - Arrow gap animation on hover

Mobile Responsiveness

Back Button Handling:

Browser/phone back button is intercepted to prevent leaving the portal:

// Handle browser/phone back button
useEffect(() => {
  if (view !== 'home') {
    window.history.pushState({ view }, '', window.location.pathname);
  }

  const handlePopState = (event) => {
    if (view !== 'home') {
      goHome();
    }
  };

  window.addEventListener('popstate', handlePopState);
  return () => window.removeEventListener('popstate', handlePopState);
}, [view]);

// Set initial history state on mount
useEffect(() => {
  window.history.replaceState({ view: 'home' }, '', window.location.pathname);
}, []);

Responsive Typography:

/* Hero title */
fontSize: clamp(28px, 7vw, 36px)

/* Section headings */
fontSize: clamp(22px, 6vw, 28px)

/* Body text */
fontSize: clamp(15px, 4vw, 16px)

Responsive Grid:

/* What to Expect cards - auto single column on mobile */
gridTemplateColumns: repeat(auto-fit, minmax(280px, 1fr))

Responsive Spacing:

/* Card padding */
padding: clamp(24px, 5vw, 36px)

/* Pullquote padding */
padding: clamp(20px, 5vw, 32px)

Insert Card Integration

The physical insert card included in every box links to the Transition Guide:

Front of card (QR code): - Links to: https://my.protocolraw.co.uk?view=transition - Text: "Scan for your personalised feeding plan"

Note: The insert card QR links to the portal transition guide, NOT to the batch proof page. Batch verification is handled by the QR codes printed directly on the pouches.

See: SOP Update Brief - Insert Card Simplification for full details.

Customer.io Integration

Template: transition_guide
Trigger: First order, +24 hours
Purpose: Remind customer about transition guide in portal

CTA Link:

https://my.protocolraw.co.uk?token={{customer.portal_token}}&view=transition

Note: Email content should be brief and drive to portal, not duplicate the transition content inline.


Credit Balance Display

Overview

The customer portal displays available credit balance when the customer has credits > ยฃ0. Credits are earned through referrals and are automatically applied at subscription renewal.

User Interface

Location: HomeView, between Quick Actions and Verification Stats

Display Conditions: - Only shown when credits.has_credits === true - Hidden when balance is ยฃ0

Components:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  Credit Balance                          ยฃ15       โ”‚
โ”‚                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚ โš    Credit expiring soon. Active              โ”‚  โ”‚  รขโ€ ย Only if has_expiring_soon
โ”‚  โ”‚   subscriptions use it automatically.        โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                    โ”‚
โ”‚  Applied automatically to your next renewal.       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Styling: - Card: White background, 24px margin bottom - Header: Montserrat Bold 18px, Espresso (#2B2523) - Balance: Montserrat Bold 28px, Forest Green (#2D5144) - Warning banner: Warm amber background (#FEF3E7), left border (#D4A574) - Info text: Inter 14px, Warm Gray (#6B6360)

API Integration

File: src/api.js

export async function getCustomerCredits(customerId) {
  const response = await fetch(`${SUPABASE_URL}/rest/v1/rpc/get_customer_credits`, {
    method: 'POST',
    headers: {
      'apikey': SUPABASE_ANON_KEY,
      'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
      'Content-Type': 'application/json',
      'Content-Profile': 'raw_ops',
    },
    body: JSON.stringify({ p_customer_id: customerId }),
  });

  if (!response.ok) {
    console.error('Failed to fetch credits');
    return null;
  }

  return response.json();
}

Note: Uses Content-Profile: raw_ops header because the function is in the raw_ops schema.

Data Loading

Credits are fetched in loadDashboard() alongside shipment data:

// In Portal.jsx loadDashboard()
if (data.customer?.id) {
  const shipmentData = await api.getActiveShipment(data.customer.id);
  setShipment(shipmentData);

  const creditData = await api.getCustomerCredits(data.customer.id);
  setCredits(creditData);
}

Payment Method Update

Overview

Customers can update their payment method directly from the portal when their subscription has payment issues or proactively.

User Interface

Payment Warning Banner:

Displayed when subscription.payment_status !== 'healthy':

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  โš รฏยธย We had trouble processing your last payment.              โ”‚
โ”‚                                                               โ”‚
โ”‚  [Update payment method]                                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Update Button Behaviour: 1. Customer clicks "Update payment method" 2. Button shows loading state 3. Portal calls Edge Function to get Seal magic link 4. Customer redirected to Seal's payment update page 5. On completion, customer returns to portal

Edge Function: get-payment-update-url

// supabase/functions/get-payment-update-url/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

serve(async (req) => {
  const { customer_id } = await req.json()

  // Get subscription from database
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )

  const { data: subscription } = await supabase
    .from('subscriptions')
    .select('seal_subscription_id')
    .eq('customer_id', customer_id)
    .single()

  if (!subscription?.seal_subscription_id) {
    return new Response(JSON.stringify({ error: 'No subscription found' }), { status: 404 })
  }

  // Get payment update URL from Seal API
  const sealResponse = await fetch(
    `https://api.sealsubscriptions.com/v1/subscriptions/${subscription.seal_subscription_id}/payment-update-url`,
    {
      headers: {
        'Authorization': `Bearer ${Deno.env.get('SEAL_API_TOKEN')}`,
      }
    }
  )

  const { url } = await sealResponse.json()

  return new Response(JSON.stringify({ url }), {
    headers: { 'Content-Type': 'application/json' }
  })
})

API Function

File: src/api.js

export async function getPaymentUpdateUrl(customerId) {
  const response = await fetch(`${SUPABASE_URL}/functions/v1/get-payment-update-url`, {
    method: 'POST',
    headers: {
      'apikey': SUPABASE_ANON_KEY,
      'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ customer_id: customerId }),
  });

  if (!response.ok) {
    throw new Error('Failed to get payment update URL');
  }

  return response.json();
}

Dunning Email Fallback

When dunning emails are sent, they include a link to the customer portal where the payment warning banner will be displayed:

CTA: "Update your payment method"
URL: https://my.protocolraw.co.uk?token={{customer.portal_token}}

Shipment Tracking

Overview

Real-time tracking information for in-transit deliveries.

Tracking Card

Displayed on HomeView when there's an active shipment:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  ๐Ÿ“ฆ Your order is on its way                                   โ”‚
โ”‚                                                               โ”‚
โ”‚  Status: Out for delivery                                     โ”‚
โ”‚  Estimated: Today by 6pm                                      โ”‚
โ”‚                                                               โ”‚
โ”‚  [Track with DPD โ†’]                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Function

export async function getActiveShipment(customerId) {
  const { data, error } = await supabase
    .from('shipments')
    .select('*')
    .eq('customer_id', customerId)
    .in('status', ['shipped', 'in_transit', 'out_for_delivery'])
    .order('created_at', { ascending: false })
    .limit(1)
    .single();

  if (error || !data) return null;
  return data;
}

Address Editing

Overview

Self-serve address updates with automatic sync to Shopify and Seal.

User Interface

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  Delivery Address                                    [Edit]   โ”‚
โ”‚                                                               โ”‚
โ”‚  John Smith                                                   โ”‚
โ”‚  123 Main Street                                              โ”‚
โ”‚  London, E1 4AB                                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Edit Modal

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  Edit Delivery Address                              [X]       โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                               โ”‚
โ”‚  Address Line 1 *                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚ 123 Main Street                                         โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                               โ”‚
โ”‚  Address Line 2                                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚ Flat 4                                                  โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                               โ”‚
โ”‚  City *                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚ London                                                  โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                               โ”‚
โ”‚  Postcode *                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย  โ”‚
โ”‚  โ”‚ E1 4AB                                                  โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                               โ”‚
โ”‚  [Cancel]                               [Save Address]        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Sync Flow

  1. Customer submits address change
  2. Portal updates raw_ops.customers table
  3. Creates record in raw_ops.address_changes for audit
  4. Make.com scenario triggered via webhook
  5. Scenario updates Shopify customer address
  6. Scenario updates Seal subscription address
  7. Sync status updated in address_changes table

48-Hour Delivery Lock System

Purpose

Prevents subscription changes when a delivery is imminent to avoid fulfillment confusion.

Lock Window

  • Applies to orders within 48 hours of scheduled ship date
  • Blocks: Skip, Reschedule, Pause, Box Size Change, Frequency Change
  • Does NOT block: View-only actions, Address changes, Payment updates

User Interface

When locked:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€รขโ€ย
โ”‚  ๐Ÿ”’ Changes temporarily locked                                 โ”‚
โ”‚                                                               โ”‚
โ”‚  Your next order ships within 48 hours. Contact support if    โ”‚
โ”‚  you need to make urgent changes.                             โ”‚
โ”‚                                                               โ”‚
โ”‚  Unlocks: Tuesday, 28 Jan at 10:00 AM                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Check

function isWithinLockWindow(nextDeliveryDate) {
  const lockWindowMs = 48 * 60 * 60 * 1000; // 48 hours
  const now = new Date();
  const deliveryDate = new Date(nextDeliveryDate);
  return (deliveryDate - now) < lockWindowMs;
}

Email Notifications

Portal Access Request

Template: portal_access Trigger: Customer requests portal access Subject: "Your Protocol Raw Portal Login"

Subscription Changes

All subscription changes trigger confirmation emails via Customer.io: - subscription_paused - subscription_resumed - subscription_cancelled - delivery_skipped - delivery_rescheduled - box_size_changed - frequency_changed

Transition Guide Reminder

Template: transition_guide
Trigger: First order, +24 hours
Subject: "Your personalised transition guide is ready"

CTA Link:

https://my.protocolraw.co.uk?token={{customer.portal_token}}&view=transition


Database Schema

portal_tokens

CREATE TABLE raw_ops.portal_tokens (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID REFERENCES raw_ops.customers(id),
  token UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
  expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '7 days'),
  used_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

customer_credits

CREATE TABLE raw_ops.customer_credits (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID REFERENCES raw_ops.customers(id),
  amount_pence INTEGER NOT NULL,
  remaining_pence INTEGER NOT NULL,
  source TEXT NOT NULL, -- 'referral', 'goodwill', 'promotion'
  status TEXT NOT NULL DEFAULT 'available',
  expires_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

subscriptions (payment status fields)

-- Relevant fields for payment status
ALTER TABLE raw_ops.subscriptions ADD COLUMN IF NOT EXISTS payment_status TEXT DEFAULT 'healthy';
-- Values: 'healthy', 'failing', 'failed'

address_changes

CREATE TABLE raw_ops.address_changes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  customer_id UUID REFERENCES raw_ops.customers(id),
  old_address JSONB,
  new_address JSONB,
  source TEXT NOT NULL, -- 'customer_portal' or 'ops_portal'
  agent_email TEXT, -- Only for ops_portal changes
  reason TEXT,
  support_ticket_id UUID,
  shopify_synced BOOLEAN DEFAULT FALSE,
  seal_synced BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

customers (v2.6 additions)

-- Added in v2.6 for first-visit detection
ALTER TABLE raw_ops.customers 
ADD COLUMN portal_first_visited_at TIMESTAMPTZ DEFAULT NULL;

COMMENT ON COLUMN raw_ops.customers.portal_first_visited_at IS 
  'Timestamp of first portal visit. Used for first-visit redirect to Transition Guide.';

-- Index for analytics (optional)
CREATE INDEX idx_customers_first_visit 
ON raw_ops.customers(portal_first_visited_at) 
WHERE portal_first_visited_at IS NOT NULL;

Key Views

v_customer_portal_data

CREATE VIEW raw_ops.v_customer_portal_data AS
SELECT
  c.id,
  c.email,
  c.first_name,
  c.last_name,
  c.address1,
  c.address2,
  c.city,
  c.province,
  c.zip,
  c.seal_subscription_id,
  c.portal_first_visited_at,  -- Added in v2.6
  json_agg(DISTINCT d.*) AS dogs,
  (SELECT COUNT(*) FROM raw_ops.orders WHERE customer_id = c.id) AS order_count
FROM raw_ops.customers c
LEFT JOIN raw_ops.dogs d ON d.customer_id = c.id
GROUP BY c.id;

API Reference

Authentication

Validate Token

POST /rest/v1/rpc/validate_portal_token
Body: { "p_token": "uuid" }
Returns: { "valid": true, "customer_id": "uuid" }

Request Portal Access

POST /rest/v1/rpc/request_portal_access
Body: { "p_email": "customer@example.com" }
Returns: { "success": true }

Session Response (v2.6)

The portal session endpoint now includes isFirstVisit flag:

{
  "success": true,
  "customer": {
    "id": "uuid",
    "first_name": "John",
    "email": "john@example.com",
    "portal_first_visited_at": null
  },
  "dogs": [
    {
      "id": "uuid",
      "dog_name": "Luna",
      "dog_weight_kg": 25,
      "life_stage": "adult",
      "activity_level": "moderate",
      "daily_grams": 400
    }
  ],
  "subscription": {...},
  "isFirstVisit": true
}

Customer Data

Get Portal Data

GET /rest/v1/v_customer_portal_data?id=eq.[customer_id]
Headers: apikey, Authorization

Get Credits

POST /rest/v1/rpc/get_customer_credits
Headers: Content-Profile: raw_ops
Body: { "p_customer_id": "uuid" }

Subscription Actions

All subscription actions route through Make.com scenarios for Seal API interaction.


Edge Functions

portal-session

Validates token, creates session, handles first-visit detection.

get-payment-update-url

Returns Seal magic link for payment method update.

Deployment

# Deploy the payment update edge function
supabase functions deploy get-payment-update-url

# Set required secrets
supabase secrets set SEAL_API_TOKEN=your_seal_token_here

Make.com Integration

Portal Webhook Scenarios

Scenario Trigger Action
PORTAL-01 Address change Sync to Shopify + Seal
PORTAL-02 Skip delivery Call Seal API
PORTAL-03 Pause subscription Call Seal API
PORTAL-04 Resume subscription Call Seal API
PORTAL-05 Box size change Call Seal API
PORTAL-06 Frequency change Call Seal API
PORTAL-07 Cancel subscription Call Seal API + send email

Deployment

Cloudflare Pages

# Build
npm run build

# Deploy
wrangler pages deploy dist --project-name=protocol-raw-customer-portal

Environment Variables

Set in Cloudflare Pages dashboard: - VITE_SUPABASE_URL - VITE_SUPABASE_ANON_KEY


Troubleshooting

Symptoms: Customer requests access but no email arrives

Investigation: 1. Check Customer.io for event delivery 2. Verify email exists in raw_ops.customers 3. Check spam/junk folder 4. Verify token created in portal_tokens

Common causes: - Email not in customers table (not a subscriber) - Customer.io event failed - Email in spam

Portal Shows "Session Expired"

Symptoms: Customer clicks link but sees expired message

Investigation:

SELECT token, expires_at, used_at
FROM raw_ops.portal_tokens
WHERE customer_id = '[customer_id]'
ORDER BY created_at DESC
LIMIT 5;

Common causes: - Token older than 7 days - Token already used (single-use) - Customer clicked old link

Subscription Actions Failing

Symptoms: Customer clicks action but nothing happens

Investigation: 1. Check browser console for errors 2. Verify Make.com scenario history 3. Check Seal API response

Common causes: - Seal subscription ID mismatch - Make.com scenario paused - Within 48-hour lock window

Credits Not Appearing

Symptoms: Customer has credit but portal shows no credit card

Investigation:

  1. Check browser console for API errors:

    Failed to fetch credits
    

  2. Verify credit exists in database:

    SELECT * FROM raw_ops.customer_credits 
    WHERE customer_id = '[customer_id]' 
    AND status = 'available' 
    AND remaining_pence > 0
    AND expires_at > NOW();
    

  3. Test the function directly:

    SELECT raw_ops.get_customer_credits('[customer_id]');
    

  4. Check API headers - must use Content-Profile: raw_ops not Accept-Profile

Common causes: - Credit expired (check expires_at) - Credit fully applied (check remaining_pence) - Credit status not 'available' (check status) - Wrong schema header in API call

Credits Showing Wrong Balance

Investigation:

-- Get all available credits
SELECT id, amount_pence, remaining_pence, status, expires_at
FROM raw_ops.customer_credits 
WHERE customer_id = '[customer_id]'
ORDER BY created_at DESC;

-- Check the view used by function
SELECT * FROM raw_ops.v_customer_credit_balance
WHERE customer_id = '[customer_id]';

Payment Update Button Not Working

Symptoms: Customer clicks "Update payment method" but nothing happens or error shown

Investigation:

  1. Check browser console for errors
  2. Check Supabase Edge Function logs:

    supabase functions logs get-payment-update-url
    

  3. Verify subscription exists:

    SELECT id, seal_subscription_id, status, payment_status
    FROM raw_ops.subscriptions
    WHERE customer_id = '[customer_id]';
    

  4. Test Edge Function directly:

    curl -X POST https://[project].supabase.co/functions/v1/get-payment-update-url \
      -H "apikey: [anon_key]" \
      -H "Content-Type: application/json" \
      -d '{"customer_id": "[customer_uuid]"}'
    

  5. Verify Seal API credentials:

    # Check if SEAL_API_TOKEN is set
    supabase secrets list
    

Common causes: - No active subscription found - Seal API token expired or invalid - Seal subscription ID not stored in our database - Edge function not deployed - CORS issues (check browser network tab)

Payment Warning Not Showing

Symptoms: Customer is in dunning but no warning banner appears

Investigation:

  1. Check subscription payment_status:

    SELECT payment_status, failed_payment_count, dunning_started_at
    FROM raw_ops.subscriptions
    WHERE customer_id = '[customer_id]';
    

  2. Verify payment_status is not 'healthy':

  3. Should be 'failing' or 'failed' to show warning

Common causes: - payment_status still 'healthy' (webhook not processed) - Subscription state out of sync with Seal

Address Changes Not Syncing

Symptoms: Address updated in portal but old address in Shopify/Seal

Investigation:

SELECT * FROM raw_ops.address_changes
WHERE customer_id = '[customer_id]'
ORDER BY created_at DESC;

Check sync status: - shopify_synced = false โ†’ Shopify update failed - seal_synced = false โ†’ Seal update failed

Common causes: - Make.com scenario error - Shopify API rate limited - Seal subscription ID missing

Transition Guide Not Showing for New Customers

Symptoms: New customer goes straight to HomeView instead of TransitionView

Investigation:

  1. Check if first visit was already recorded:

    SELECT portal_first_visited_at
    FROM raw_ops.customers
    WHERE id = '[customer_id]';
    

  2. If portal_first_visited_at is already set, the redirect won't happen

  3. Check browser console for isFirstVisit flag in session response

Common causes: - Customer visited portal before v2.6 deployment - Edge function not updated to include isFirstVisit flag - Frontend not handling the redirect

Symptoms: ?view=transition URL parameter ignored

Investigation:

  1. Check browser console for URL parsing errors
  2. Verify the URL includes both token and view parameters
  3. Check if authentication succeeds before view is set

Common causes: - Token validation failing (view param processed but user logged out) - Frontend useEffect not running (React render issue)


Version History

Version Date Changes
2.6 2026-01-21 Transition Guide: personalised 10-day feeding schedule, first-visit detection, deep linking, mobile back button handling
2.5 2026-01-17 Payment method update via Seal magic link
2.4 2026-01-10 Credit balance display
2.3 2026-01-05 Documentation consolidation
2.2 2025-12-20 Self-serve address editing
2.1 2025-12-10 Shipment tracking card
2.0 2025-11-30 Initial elevated design release

  • SOP-SUB-00: Subscription State Management (dunning flow, payment webhooks)
  • SOP CS-00: Customer Operations System (includes portal specifications)
  • SOP-REF-01 v2.1: Referral System (credit ledger integration)
  • Protocol_Raw_Email_Design_System_v1_0.md: Email templates
  • Protocol_Raw_Proof_Portal_Documentation_v1_0.md: Batch verification portal
  • Protocol_Raw_Operations_Portal_Documentation_v3_6.md: Internal operations portal
  • Visual-Identity-Guide-v2_2-Component-Patterns.md: Component styling guidelines

Document Owner: Protocol Raw Operations Team
Last Reviewed: January 21, 2026
Next Review: April 21, 2026
Version: 2.6
Status: Production Ready โœ…


End of Protocol Raw Customer Portal Documentation v2.6