Skip to content

Protocol Raw Operations Portal Documentation

Version: 3.8
Last Updated: February 17, 2026
Status: Production Ready
Portal URL: https://ops.protocolraw.co.uk


What's New in v3.8

Terminology & Schema Corrections: - ✅ Batch status terminology alignedCLEAREDRELEASED, QA_FAILQUARANTINE (matches live database, SOP-01 v4.3, config.js, and Systems Overview v2.0) - ✅ Database schema updated — Removed stale CHECK constraint from documentation (live database has no status enum constraint); updated column names to match actual schema - ✅ Batch lifecycle diagram corrected — All status references now use canonical values - ✅ Version aligned to live portal — Documentation now matches portal v3.8 (Refund Actions)

From v3.7 (February 2026): - ✅ Refund action buttons in Support Workstation - ✅ Config version bumped to v3.8

From v3.6 (January 20, 2026): - ✅ Conversation Thread Panel - Full message history displayed in ticket detail view - ✅ Email Threading - In-Reply-To and References headers for proper inbox threading - ✅ [Support] Subject Prefix - All outbound emails prefixed to distinguish from marketing - ✅ Editable Subject Line - Agent can customize subject before sending - ✅ Message History Tracking - ticket_messages table records full back-and-forth - ✅ Unified Email Sending - send-support-email Edge Function handles all outbound

Database Updates: - ✅ email_message_id and email_references columns on support_tickets - ✅ ticket_messages table with direction, sender, delivery_id fields - ✅ Updated v_support_queue view with message count

From v3.5 (January 19, 2026): - ✅ Agent Review tab - Human-in-the-loop validation interface for AI agent decisions - ✅ Shadow Mode workflow - All AI decisions queued for human review before autonomous mode - ✅ Confidence badges - Color-coded display (green >85%, yellow 70-85%, red <70%) - ✅ cs_agent_decisions table - New database table for AI decision tracking

From v3.4 (January 13, 2026): - ✅ Documentation consolidation - merged v2.0 and v3.3 into single comprehensive document - ✅ Pack Day, Fulfillment, and Batch Creation workflows restored - ✅ Full troubleshooting guide restored (20+ scenarios)

From v3.3 (December 10, 2025): - ✅ Customer Search - Real-time search by name, email, or phone with debounced queries - ✅ Customer Profile Panel - Full customer view with subscription management, order history, address - ✅ Ticket Assignment - Assign tickets to agents with visible queue badges - ✅ Outbound Tickets - Proactively contact customers from Customer Profile - ✅ Awaiting Reply Filter - Dedicated queue filter for outbound tickets awaiting customer response


Table of Contents

  1. Overview
  2. Access & Authentication
  3. Portal Structure
  4. Architecture Overview
  5. Pack Day (SOP PACK)
  6. Fulfillment (SOP 0Y)
  7. Batch Creation (SOP 01)
  8. Support Workstation
  9. Agent Review (Shadow Mode)
  10. Live Chat
  11. Analytics
  12. Database Views & Tables
  13. Edge Functions
  14. Make.com Webhooks
  15. System Architecture
  16. Security & Best Practices
  17. Troubleshooting
  18. Maintenance & Updates
  19. Keyboard Shortcuts
  20. Deployment
  21. Appendix
  22. For LLM Assistants

Overview

Purpose

The Protocol Raw Operations Portal is a unified interface for managing core business operations. Built as a tech-first, AI-native platform, it demonstrates operational sophistication and provides a single hub for packing, fulfillment, batch tracking, customer support, AI agent validation, and business intelligence.

Key Features

  • Pack Day Automation (SOP PACK) - Weather-based PCM calculation for Tuesday/Thursday packs
  • Fulfillment Management (SOP 0Y) - Batch dispatch recording with CSV upload
  • Batch Creation (SOP 01) - Production batch registration with QA_HOLD workflow
  • Support Workstation - Full customer operations with ticket management, search, conversation threading, and proactive outreach
  • Agent Review - Shadow mode validation for AI customer service decisions
  • Live Chat - Real-time customer communication
  • Analytics Integration - Metabase dashboard access
  • Protocol Raw Branding - Consistent visual identity throughout
  • Responsive Design - Works on desktop, tablet, and mobile
  • Real-time Updates - Direct Supabase database connectivity

Design Principles

  1. Scale First - Built for 100,000+ customers from day one
  2. Zero Dependencies - No npm packages, no build step, no dependency rot
  3. Modular by Default - Each feature is a self-contained ES6 module
  4. State-Driven UI - Centralized state with pub/sub for reactive updates
  5. Brand Consistent - CSS tokens derived from Visual Identity Guide v2.1
  6. Systematic, not emotional - Evidence-based operations
  7. Proof, not promises - Verifiable safety at every step

Access & Authentication

Portal URL

Primary: https://ops.protocolraw.co.uk
Backup: https://protocol-raw-ops.pages.dev

Authentication Method

The portal uses Cloudflare Access with Google OAuth for authentication. Only authorized email addresses can access the portal.

Login Process

  1. Navigate to https://ops.protocolraw.co.uk
  2. Click "Sign in with Google"
  3. Authenticate with your authorized Google account
  4. Session lasts 24 hours before re-authentication required

Managing Access

Add/Remove Users: 1. Cloudflare Dashboard → Zero Trust → Access → Applications 2. Find "Protocol Raw Operations Portal" application 3. Edit policy → Add or remove email addresses 4. Save changes

⚠ï¸Â Security Note

The portal contains your Supabase service role key with full database access. It is protected by Cloudflare Access authentication. Never share login credentials or disable authentication.


Portal Structure

The portal uses a tab-based navigation system with seven main sections:

  1. 🎯 Pack Day (SOP PACK) - Automated packing workflow
  2. 📦 Fulfillment (SOP 0Y) - Dispatch recording
  3. 🧪 Batches (SOP 01) - Production batch registration
  4. 📊 Inventory - Stock levels and allocation
  5. 🎧 Support Workstation - Customer operations hub
  6. 🤖 Agent Review - AI decision validation (shadow mode)
  7. 💬 Live Chat - Real-time customer communication

Visual Design

Typography: - Headlines: Montserrat Bold 700 - Body text: Inter Regular 400 - UI elements: Inter Medium 500

Colors: - Background: Old Lace (#FEFDF7) - Surface: Spring Wood (#F2EEE3) - Text: Heavy Metal (#323531) - Muted text: Friar Gray (#7C7C78) - Primary CTA: Terracotta (#D48762) - Secondary: Nandor (#4B685B)

Layout: - Max width: 1200px (centered) - Generous padding: 40px desktop, 20px mobile - Border radius: 12px - Responsive breakpoint: 768px


Architecture Overview

Technology Stack

Layer Technology Purpose
Hosting Cloudflare Pages Static file serving, edge caching
Auth Cloudflare Access Zero-trust authentication
Database Supabase PostgreSQL Data storage, views, RPC functions
API Supabase REST + Edge Functions Data operations
Automation Make.com Webhook-triggered workflows
Subscriptions Seal Subscriptions Shopify subscription management
Email Customer.io Transactional email delivery
Frontend Vanilla JS (ES6 modules) No framework overhead
Fonts Google Fonts Montserrat + Inter

Request Flow

User Action
Module Function (e.g., support.subscriptionAction)
API Layer (api.js) → Supabase REST/RPC/Edge Function
    ↓                    or
                   Make.com Webhook → Seal API / Customer.io
State Update (state.js)
UI Re-render (subscribers notified)

File Structure

/
├── index.html
├── css/
│   └── agent-review.css        # Agent Review tab styles
└── js/
    ├── app.js                  # Main application, module orchestration
    ├── api.js                  # Supabase client, webhooks
    ├── config.js               # Configuration (v3.8)
    ├── state.js                # Centralized state management
    └── modules/
        ├── packday.js          # Pack Day tab
        ├── fulfillment.js      # Fulfillment tab
        ├── batches.js          # Batches tab
        ├── inventory.js        # Inventory tab
        ├── support.js          # Support Workstation
        ├── agent-review.js     # Agent Review tab (v3.5)
        └── chat.js             # Live Chat

Pack Day (SOP PACK)

Purpose

Automate the intelligent packing workflow for Tuesday and Thursday dispatch days. Calculate PCM (Phase Change Material) requirements based on real weather forecasts to ensure frozen delivery.

Pack Schedule

Tuesday Dispatch: - Calculate PCM: 9:00 AM Tuesday - Pack orders: 10:00 AM - 4:00 PM - Courier collection: 4:00-6:00 PM - Customer delivery: Wednesday/Thursday (48h)

Thursday Dispatch: - Calculate PCM: 9:00 AM Thursday - Pack orders: 10:00 AM - 4:00 PM - Courier collection: 4:00-6:00 PM - Customer delivery: Friday/Saturday (48h)

Workflow

Step 1: Calculate PCM Requirements

When: Pack morning (9:00 AM on Tuesday or Thursday)

How: 1. Navigate to "Pack Day (SOP PACK)" tab 2. Click "🧊 Calculate PCM for Today's Packs" button 3. System executes: - Fetches all orders with export_state = 'sent' - Filters orders without existing packing instructions - Calls OpenWeatherMap API for each delivery postcode - Determines max temperature over next 48 hours - Calculates PCM requirements per packaging specification - Creates packing instructions in database - Returns results with risk levels

Expected Results:

✅ Success!
Processed 23 orders
23 successful, 0 failed

Results:
#1234 - 6 packs - STANDARD
#1235 - 9 packs - MEDIUM  
#1236 - 11 packs - HIGH

Risk Levels: - STANDARD (<20°C) - Base PCM requirement, no logger - MEDIUM (20-29°C) - Increased PCM, logger required - HIGH (≥30°C) - Maximum PCM, logger required

Troubleshooting:

Button doesn't respond: - Check browser console (F12) for errors - Verify Edge Function is deployed: calculate-pack-day-instructions - Check Supabase Edge Functions logs

Error: "No orders found": - Verify orders exist with export_state = 'sent' - Check SOP 0X (Export to Fulfillment) ran successfully - Query database: SELECT * FROM raw_ops.orders WHERE export_state = 'sent'

API error: - Check OpenWeatherMap API status - Verify API key in Edge Function is valid - Check Edge Function logs for detailed error

Step 2: View Pack Queue

When: After PCM calculation completes

How: 1. Click "📋 Open Pack Queue" button 2. System opens Metabase dashboard (when configured) 3. Dashboard shows: - Orders sorted by risk level (HIGH → MEDIUM → STANDARD) - PCM pack count per order - Delivery postcode - Customer name - Special notes

Alternative (Pre-Metabase):

Query the database directly:

SELECT 
  order_number_label,
  customer_name,
  delivery_postcode,
  box_size,
  pcm_packs_count,
  risk_level,
  ambient_forecast_max,
  logger_required,
  special_notes
FROM raw_ops.v_pack_queue_with_batch
ORDER BY 
  CASE risk_level 
    WHEN 'HIGH' THEN 1 
    WHEN 'MEDIUM' THEN 2 
    WHEN 'STANDARD' THEN 3 
  END,
  exported_at ASC;

Step 3: Pack & Dispatch

Process: 1. Follow pack queue instructions for each order 2. Record quality checks per SOP PACK-01 3. Use correct number of PCM packs 4. Insert temperature logger if required 5. Record dispatch via Fulfillment tab (SOP 0Y)

Quality Metrics: - Pack time: <10 mins (8kg/12kg), <12 mins (16kg) - Weight variance: ±5% tolerance - Temperature: Product at ≤-25°C at pack time

Technical Details

Edge Function: calculate-pack-day-instructions

Database Tables: - raw_ops.packing_instructions - Stores PCM requirements per order - raw_ops.orders - Source for orders ready to pack - raw_ops.v_pack_queue_with_batch - View combining instructions with batch data

API Integration: - OpenWeatherMap API (via Edge Function) - 1000 free calls/day - 48-hour forecast window - Postcode-based temperature lookup

Authentication: - Uses Supabase Anon Key (public, safe for browser) - Edge Function handles secure API calls


Fulfillment (SOP 0Y)

Purpose

Record dispatch confirmations for orders. Supports batch processing of dispatches with CSV upload to trigger automated courier integration and customer notifications.

Workflow

Step 1: Add Dispatches to Batch

When: After packing each order

How: 1. Navigate to "Fulfillment (SOP 0Y)" tab 2. Enter dispatch details: - Order ID: Shopify order ID (e.g., 12294292865399) - Tracking Number: Courier tracking number - Courier: Select from DPD, Royal Mail, Evri, UPS - SKU: Defaults to STARTER-8KG (or specify) - Qty: Number of units (usually 1) 3. Click "➕ Add to Batch" 4. Order appears in Current Batch table 5. Repeat for all orders packed today

Features: - LocalStorage Persistence: Batch survives page refresh - Duplicate Prevention: Can't add same Order ID twice - Remove Individual Orders: Remove button per row - Clear Batch: Clear all at once with confirmation

Example Batch:

Order ID Tracking Number Courier SKU Qty
12294292865399 DPD123456789 DPD STARTER-8KG 1
12294292865400 DPD123456790 DPD STARTER-12KG 1
12294292865401 DPD123456791 DPD STARTER-8KG 1

Step 2: Submit Batch

When: End of pack day (4:00-6:00 PM)

How: 1. Review Current Batch table (check for errors) 2. Click "🚀 Submit Batch to SOP 0Y" 3. Confirm submission in popup dialog 4. System executes: - Generates CSV file with all dispatches - Filename: dispatch_batch_YYYY-MM-DD_timestamp.csv - Uploads to Supabase Storage: dispatch-files/inbox/ - Clears batch from localStorage - Shows success message with filename

CSV Format:

external_order_id,dispatch_time_utc,courier,service,tracking_number,sku,qty,parcel_count,notes,status
12294292865399,2025-11-13T16:23:45.123Z,DPD,Standard,DPD123456789,STARTER-8KG,1,1,,DISPATCHED
12294292865400,2025-11-13T16:24:12.456Z,DPD,Standard,DPD123456790,STARTER-12KG,1,1,,DISPATCHED

Downstream Automation:

Once CSV is uploaded, Make.com scenario (SOP 0Y) automatically: 1. Detects new file in dispatch-files/inbox/ 2. Parses CSV and validates data 3. Updates raw_ops.shipments table 4. Creates courier tracking records 5. Triggers customer notification emails 6. Moves file to dispatch-files/processed/

Step 3: Check Recent Activity

Purpose: Verify orders are ready for dispatch

How: 1. Click "Check Recent Orders" button 2. System queries last 20 orders with status queued or sent 3. Table displays: - Order ID - Export state (queued/sent) - Order date

Use Cases: - Verify SOP 0X exported orders successfully - Check order count matches expected pack day volume - Identify any orders stuck in queue

Technical Details

Storage Location: - Bucket: dispatch-files - Folder: inbox/ (for new files) - Folder: processed/ (after Make.com processes)

Database Tables: - raw_ops.orders - Source for recent activity - raw_ops.shipments - Updated by Make.com after CSV processing

Authentication: - Uses Supabase Service Role Key (full access, portal protected by Cloudflare Access)

File Naming Convention: - Format: dispatch_batch_YYYY-MM-DD_UnixTimestamp.csv - Example: dispatch_batch_2025-11-13_1731513825123.csv

Troubleshooting

"Order ID already in batch" error: - Order was already added to current batch - Check Current Batch table - Remove duplicate if needed

Submit fails with "Upload failed: 403": - Service Role Key may be invalid - Check Supabase Storage bucket permissions - Verify bucket dispatch-files exists with inbox/ folder

Submit succeeds but Make.com doesn't process: - Check Make.com scenario is active - Verify webhook URL is correct - Check scenario execution history for errors


Batch Creation (SOP 01)

Purpose

Register new production batches with automatic dual ID generation, QA_HOLD status, and proof portal integration.

Workflow

Create New Batch

When: Production batch arrives from co-packer

How: 1. Navigate to "Batches (SOP 01)" tab 2. Enter batch details: - Production Date: When batch was produced - Quantity (kg): Weight of batch 3. Click "Create Batch" 4. System executes: - Auto-generates internal batch code - Sets status: QA_HOLD - Sets partner: COPACKER-A - Sets COGS: £3.50/kg - Generates dual IDs (internal batch code + public batch ID) - Creates QR code for proof portal - Inserts record into raw_ops.batches - Shows success message

Example:

Input: - Batch Code: BATCH-20251113-001 - Production Date: 2025-11-13 - Quantity: 500 kg

Creates:

{
  "batch_code": "BATCH-20251113-001",
  "public_batch_id": "PR-A711BCF6",
  "production_date": "2025-11-13",
  "expiry_date": "2026-11-13",
  "kg_produced": 500.00,
  "status": "QA_HOLD",
  "partner": "COPACKER-A",
  "cogs_per_kg": 3.50
}

View Recent Batches

Purpose: Monitor batch status and inventory levels

How: 1. Click "View Recent Batches" button 2. System queries last 10 batches, sorted by production date 3. Table displays: - Batch Code - Public Batch ID - Production Date - Status (QA_HOLD, RELEASED, QUARANTINE, DEPLETED) - Quantity (kg)

Status Meanings: - QA_HOLD: Awaiting lab results (cannot be sold or allocated) - RELEASED: Lab tests passed, available for dispatch - QUARANTINE: Failed testing — do not use - DEPLETED: No stock remaining, archived

Batch Lifecycle

1. BATCH CREATED
   └─> Status: QA_HOLD

2. LAB SAMPLE SENT
   └─> Co-packer sends sample to UKAS lab

3. LAB RESULTS RECEIVED
   └─> Email with PDF arrives

4. SOP 01 AUTOMATION
   └─> Make.com parses PDF
   └─> Checks: Salmonella ABSENT, Listeria ABSENT
   └─> If PASS: Status → RELEASED
   └─> If FAIL: Status → QUARANTINE (batch quarantined)

5. BATCH AVAILABLE FOR SALE
   └─> Inventory system can allocate (SOP-INV-01)
   └─> Orders can be fulfilled

6. BATCH DEPLETED
   └─> All kg allocated to orders
   └─> Status → DEPLETED

Technical Details

Database Table: raw_ops.batches

Schema (simplified — see Supabase for full current schema):

CREATE TABLE raw_ops.batches (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  batch_code TEXT UNIQUE NOT NULL,
  public_batch_id TEXT UNIQUE NOT NULL,
  production_date DATE NOT NULL,
  expiry_date DATE,
  kg_produced NUMERIC NOT NULL CHECK (kg_produced > 0),
  qty_on_hand NUMERIC CHECK (qty_on_hand >= 0),
  qty_reserved NUMERIC CHECK (qty_reserved >= 0),
  status TEXT NOT NULL,  -- QA_HOLD, RELEASED, QUARANTINE, DEPLETED (no CHECK constraint)
  partner TEXT NOT NULL,
  cogs_per_kg_gbp NUMERIC CHECK (cogs_per_kg_gbp >= 0),
  total_cost_gbp NUMERIC CHECK (total_cost_gbp >= 0),
  formulation_id UUID REFERENCES raw_ops.formulations(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  released_at TIMESTAMPTZ,
  lab_cert_url TEXT
);

Note: The status column has no database-level CHECK constraint. Status transitions are enforced by application logic (SOP-01 triggers). The canonical status values are defined in config.js under BATCH_STATUSES: QA_HOLD, RELEASED, DEPLETED, QUARANTINE.

Authentication: - Uses Supabase Service Role Key (INSERT permission required)

Validation Rules: - Batch code must be unique - Production date cannot be in future - Quantity must be positive - COGS must be positive

Troubleshooting

"Batch code already exists" error: - Batch with this code already in database - Check Recent Batches to confirm - Use different batch code or verify co-packer confirmation

Create fails with "Permission denied": - Service Role Key may be invalid - Check Supabase console for key - Verify raw_ops.batches table exists

Expiry date incorrect: - System auto-calculates as production date + 12 months - Check production date was entered correctly - To manually adjust: Update in Supabase dashboard

Batch stuck in QA_HOLD: - Lab results not yet received - Check co-packer sent sample to lab - Check email for lab results PDF - Verify Make.com SOP 01 scenario is active - Check scenario execution history for parsing errors


Support Workstation Module

File: js/modules/support.js
Purpose: Full-featured customer operations workstation with ticket management, customer search, conversation threading, and proactive outreach

Layout (v3.6)

┌─────────────────────────────────────────────────────────────────────────┐
│  Support Workstation                              [Refresh Queue]       │
├─────────────────────────────────────────────────────────────────────────┤
│  [ðŸâ€Â Search customers by name, email, or phone...]  [Clear]             │
├─────────────────────────────────────────────────────────────────────────┤
│  Customer Profile Panel (when customer selected from search)            │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │ Customer Name        [📤 Contact Customer] [✕]                  │   │
│  │ email@example.com                                                │   │
│  │ Customer since: Jan 2025 · 5 orders · LTV: £450.00              │   │
│  ├─────────────────────────────────────────────────────────────────┤   │
│  │ Subscription: Active                                            │   │
│  │ Next: Mon 16 Dec · Every 2 weeks · 8kg                          │   │
│  │ [Skip] [Pause] [Change Box ▾] [Change Frequency ▾]              │   │
│  ├─────────────────────────────────────────────────────────────────┤   │
│  │ Address: 123 Example St, London, E1 1AA                         │   │
│  ├─────────────────────────────────────────────────────────────────┤   │
│  │ Order History                                                    │   │
│  │ #1234 · 10 Dec · PAID · £89.00                                  │   │
│  │ #1198 · 26 Nov · PAID · £89.00                                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────────────────────┤
│  [Open: 5]  [High: 2]  [Resolved Today: 12]  [Avg: 15m]                │
├──────────────────┬──────────────────────────────────────────────────────┤
│  Queue List      │  Response Panel                                      │
│  ┌────────────┐  │  ┌─────────────────────────────────────────────────┐ │
│  │[All][📧][💬]│  │  │ Customer Context           Assigned To [▾]     │ │
│  │[📤 Awaiting]│  │  │ Name · Email · Dog · Sub   Waiting: 5m         │ │
│  ├────────────┤  │  ├─────────────────────────────────────────────────┤ │
│  │ Customer 1 │  │  │ CONVERSATION THREAD (v3.6)                      │ │
│  │ 5m · High  │  │  │ ┌───────────────────────────────────────────┐   │ │
│  │ 👤 Anton   │  │  │ │ â†Â john@example.com (10:15)                │   │ │
│  ├────────────┤  │  │ │ "Hi, I have a question about my order..." │   │ │
│  │ Customer 2 │  │  │ ├───────────────────────────────────────────┤   │ │
│  │ 15m · Med  │  │  │ │ → Sophie (10:45)                          │   │ │
│  │ 📤 outbound│  │  │ │ "Thanks for reaching out! I can help..."  │   │ │
│  │ Awaiting   │  │  │ ├───────────────────────────────────────────┤   │ │
│  ├────────────┤  │  │ │ â†Â john@example.com (11:02)                │   │ │
│  │ Customer 3 │  │  │ │ "Thanks, but I still have questions..."   │   │ │
│  │ 45m · Norm │  │  │ └───────────────────────────────────────────┘   │ │
│  └────────────┘  │  ├─────────────────────────────────────────────────┤ │
│                  │  │ AI Triage: Category · Confidence · Action       │ │
│                  │  ├─────────────────────────────────────────────────┤ │
│                  │  │ Subject: [Support] Re: My order question        │ │
│                  │  ├─────────────────────────────────────────────────┤ │
│                  │  │ Draft Response                                  │ │
│                  │  │ [textarea]                                      │ │
│                  │  ├─────────────────────────────────────────────────┤ │
│                  │  │ [Send Response] [Close Without Sending]         │ │
│                  │  └─────────────────────────────────────────────────┘ │
└──────────────────┴──────────────────────────────────────────────────────┘

Conversation Thread Panel (v3.6)

The Support Workstation now displays the full conversation history for each ticket:

Feature Status Notes
Inbound messages ✅ Built Customer emails with left alignment
Outbound messages ✅ Built Our replies with right alignment
Chronological order ✅ Built Oldest first
Sender display ✅ Built Customer email or persona name
Timestamp ✅ Built Relative time (e.g., "2h ago")
Message count badge ✅ Built Shows thread depth in queue

Data Source: raw_ops.ticket_messages

Visual Styling: - Inbound (customer): Left-aligned, gray background - Outbound (us): Right-aligned, green background

Email Response Features (v3.6)

Feature Status Notes
Editable subject line ✅ Built Pre-filled with [Support] prefix
[Support] prefix auto-added ✅ Built Distinguishes from marketing
Email threading ✅ Built In-Reply-To + References headers
Persona sign-off ✅ Built Sophie/Tom/Lucy auto-appended
Response logged to thread ✅ Built Stored in ticket_messages
Panel clears after send ✅ Built Ready for next ticket

Queue Filters

Filter Description
All All open tickets (inbound + outbound)
📧 Email Inbound email tickets only
💬 Chat Chat escalations only
📤 Awaiting Outbound tickets awaiting customer reply

Real-time search with 300ms debounce:

// Search triggers after 300ms of no typing
// Returns: customer_id, first_name, last_name, email, phone,
//          subscription_status, total_orders, lifetime_value

Search fields: first_name, last_name, email, phone (case-insensitive ILIKE)

Customer Profile Panel

Displays when customer selected from search:

  • Header: Name, email, customer since, order count, LTV
  • Subscription: Status, next delivery, box size, frequency, action buttons
  • Address: Shipping address
  • Order History: Last 10 orders with status and total
  • Contact Button: Opens outbound ticket modal

Ticket Assignment

  • Agents Table: raw_ops.agents with id, name, email, role, is_active
  • Dropdown: Shows active agents, updates ticket on selection
  • Queue Badge: Blue "👤 Anton" badge on assigned tickets
  • Persistence: Stored in support_tickets.assigned_to

Outbound Tickets

Proactive customer contact flow:

  1. Search for customer → Click "📤 Contact Customer"
  2. Modal opens with customer info pre-filled
  3. Select reason: Delivery Issue, Order Issue, Payment/Billing, Account Update, General Inquiry
  4. Enter subject and message
  5. Click "Send Email & Create Ticket"
  6. Ticket created with is_outbound=true, status='awaiting_reply'
  7. Email sent via Make.com → Customer.io
  8. Ticket appears in "Awaiting" filter with purple styling

Outbound Ticket Flow:

Customer Profile
[📤 Contact Customer]
Modal: Reason + Subject + Message
create_outbound_ticket() RPC
Make.com Webhook → Customer.io Email
Ticket in queue (status: awaiting_reply)
Customer replies → Inbound flow (status: open)

Email Send Flow (v3.6)

Agent clicks "Send Response"
Portal calls send-support-email Edge Function
Edge Function:
1. Fetches ticket email_message_id
2. Builds headers: In-Reply-To + References
3. Prepends [Support] to subject if missing
4. Sends via Customer.io Transactional API
5. Inserts message into ticket_messages
6. Updates ticket status
Customer receives threaded email

Database Dependencies

Views: - public.v_support_queue - Open tickets with subscription context, assignment, outbound fields, message count

Tables: - raw_ops.support_tickets - Ticket records (includes is_outbound, outbound_reason, assigned_to, email_message_id) - raw_ops.ticket_messages - Conversation thread (v3.6) - raw_ops.agents - Agent roster - raw_ops.subscriptions - Subscription records - raw_ops.subscription_actions - Action log - raw_ops.orders - Order history - raw_ops.customers - Customer records

RPC Functions: - public.search_customers(search_term, result_limit) - Customer search - public.get_customer_profile(p_customer_id) - Full customer profile - public.get_customer_orders(p_customer_id, p_limit) - Order history - public.get_active_agents() - Active agents list - public.assign_ticket(p_ticket_id, p_assigned_to) - Assign ticket - public.create_outbound_ticket(...) - Create outbound ticket - public.log_subscription_action(...) - Log and sync subscription changes - public.get_ticket_messages(p_ticket_id) - Get conversation thread (v3.6)

Edge Functions: - send-support-email - Send response with threading (v3.6)


Agent Review Module

File: js/modules/agent-review.js
Styles: css/agent-review.css
Purpose: Human-in-the-loop validation interface for AI agent decisions during shadow mode

Purpose

The Agent Review tab provides a validation interface for AI customer service decisions before enabling autonomous responses. All AI decisions are queued for human review, allowing operators to approve correct decisions or reject incorrect ones with feedback for training.

Accessing

  1. Navigate to Ops Portal → 🤖 Agent Review tab
  2. Badge shows count of pending reviews
  3. Click Refresh to load latest queue

Review Workflow

1. AI processes inbound email → generates decision
2. Decision written to `cs_agent_decisions` with `review_status: 'pending'`
3. Decision appears in Agent Review queue
4. Human reviews:
   - Category assignment
   - Confidence score
   - AI reasoning
   - Draft response
   - Proposed actions
5. Human clicks **Approve** or **Reject**
6. Approved: Decision marked as validated
   Rejected: Feedback captured for training

UI Elements

Element Description
Review Card Shows AI decision details
Confidence Badge Color-coded (green >85%, yellow 70-85%, red <70%)
Queue Age Time since AI processed
Approve Button Mark decision as correct
Reject Button Mark incorrect with feedback prompt

Database Table

CREATE TABLE raw_ops.cs_agent_decisions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  ticket_id UUID REFERENCES raw_ops.support_tickets(id),

  -- AI Processing
  category TEXT,
  confidence_score NUMERIC(3,2),
  reasoning TEXT,
  draft_response TEXT,
  proposed_actions JSONB DEFAULT '[]',
  escalation_reason TEXT,

  -- Processing timestamps
  processed_at TIMESTAMPTZ DEFAULT NOW(),

  -- Review status
  review_status TEXT DEFAULT 'pending' 
    CHECK (review_status IN ('pending', 'approved', 'rejected')),
  reviewed_at TIMESTAMPTZ,
  reviewed_by TEXT,
  rejection_reason TEXT,
  rejection_feedback TEXT,

  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes
CREATE INDEX idx_cs_agent_decisions_review_status 
  ON raw_ops.cs_agent_decisions(review_status) 
  WHERE review_status = 'pending';

CREATE INDEX idx_cs_agent_decisions_ticket 
  ON raw_ops.cs_agent_decisions(ticket_id);

JavaScript Module

File: js/modules/agent-review.js

Key Functions:

Function Description
loadQueue() Fetch pending decisions from database
renderQueue() Display decisions as review cards
approveDecision(id) Mark decision as approved
rejectDecision(id) Mark rejected with feedback
refresh() Reload queue and update badge
updateBadge() Update pending count in tab

App.js Integration

// Import
import agentReview from './modules/agent-review.js';

// Add to modules registry
const modules = {
  // ... other modules
  agentReview,
};

// Add to tab map
const tabModuleMap = {
  // ... other tabs
  agentReview: 'agentReview',
};

// Expose globally
window.AgentReview = agentReview;

Shadow Mode → Autonomous Mode Transition

Shadow Mode (Current): - All AI decisions queued for review - No automatic responses sent - Human approves/rejects each decision - Builds confidence in AI accuracy

Transition Criteria: - 95%+ approval rate over 100+ decisions - No critical errors in last 50 decisions - All edge cases documented

Autonomous Mode (Future): - High-confidence decisions auto-execute - Low-confidence still escalated - Human reviews exceptions only

Next Steps for Agent Review

  1. Wire up Make.com AI triage to insert into cs_agent_decisions
  2. Build confidence through shadow mode validation
  3. Define autonomous mode criteria
  4. Implement auto-execute for high-confidence decisions

Live Chat Module

File: js/modules/chat.js
Purpose: Real-time customer communication with seamless escalation to ticketed support

Features

  • Real-time messaging via Supabase Realtime
  • Customer identification and context loading
  • Chat transcript history
  • Escalation to Support Workstation tickets
  • Typing indicators
  • Read receipts

Technical Details

See SOP CS-02: Live Chat System v1.4 for complete documentation.


Analytics

Purpose

Access Metabase business intelligence dashboards for operational insights and performance tracking.

Available Dashboards

Pack Queue with PCM Requirements - Orders ready to pack, sorted by risk level - PCM pack count per order - Delivery postcode and forecast temperature - Logger requirement indicator

Batch Status & Inventory - Current inventory levels (kg available) - Batches in QA_HOLD awaiting clearance - Expiry dates and batch rotation - FEFO (First Expired, First Out) compliance

Fulfillment Performance - Dispatch volume by day/week - Pack time metrics and quality checks - Courier performance and tracking - On-time delivery rates

CAC & LTV Metrics - Customer acquisition cost - Lifetime value projections - Box-2 retention rates - Cohort analysis

Access

How: 1. Navigate to "Analytics" tab 2. Click "Open Metabase Dashboard" 3. Opens in new browser tab 4. Authenticate with Metabase credentials (if required)

Metabase URL: (Configured in index.html)

Configuration

To add Metabase URL: 1. Open index.html in text editor 2. Find line: onclick="window.open('YOUR_METABASE_URL', '_blank')" 3. Replace YOUR_METABASE_URL with actual Metabase dashboard URL 4. Save and re-upload to Cloudflare Pages

Example:

onclick="window.open('https://metabase.protocolraw.co.uk/dashboard/1', '_blank')"


Database Views & Tables

Core Tables

Table Purpose
raw_ops.batches Production batch tracking
raw_ops.orders Customer order records
raw_ops.shipments Dispatch and tracking data
raw_ops.packing_instructions Pack Day PCM requirements
raw_ops.pack_quality_checks Quality control metrics
raw_ops.temperature_logger_data Post-delivery temperature audits
raw_ops.formulations Recipe management
raw_ops.customers Customer records
raw_ops.subscriptions Subscription records
raw_ops.support_tickets Support ticket records
raw_ops.ticket_messages Ticket conversation thread (v3.6)
raw_ops.ticket_notes Internal agent notes
raw_ops.agents Agent roster
raw_ops.cs_agent_decisions AI agent decisions (v3.5)
raw_ops.config System configuration

Tables (v3.3+)

raw_ops.agents

CREATE TABLE raw_ops.agents (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name TEXT NOT NULL,
    email TEXT,
    role TEXT DEFAULT 'agent',  -- agent, admin, supervisor
    is_active BOOLEAN DEFAULT true,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_agents_active ON raw_ops.agents(is_active) WHERE is_active = true;

raw_ops.support_tickets (columns added v3.3, v3.6)

-- v3.3 additions
ALTER TABLE raw_ops.support_tickets 
ADD COLUMN IF NOT EXISTS is_outbound BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS outbound_reason TEXT,
ADD COLUMN IF NOT EXISTS assigned_to TEXT;

-- v3.6 additions
ALTER TABLE raw_ops.support_tickets
ADD COLUMN IF NOT EXISTS email_message_id TEXT,
ADD COLUMN IF NOT EXISTS email_references TEXT;

CREATE INDEX idx_support_tickets_outbound 
ON raw_ops.support_tickets(is_outbound, status) 
WHERE is_outbound = true;

CREATE INDEX idx_support_tickets_assigned 
ON raw_ops.support_tickets(assigned_to) 
WHERE assigned_to IS NOT NULL;

Tables (v3.5)

raw_ops.cs_agent_decisions

CREATE TABLE raw_ops.cs_agent_decisions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  ticket_id UUID REFERENCES raw_ops.support_tickets(id),

  -- AI Processing
  category TEXT,
  confidence_score NUMERIC(3,2),
  reasoning TEXT,
  draft_response TEXT,
  proposed_actions JSONB DEFAULT '[]',
  escalation_reason TEXT,

  -- Processing timestamps
  processed_at TIMESTAMPTZ DEFAULT NOW(),

  -- Review status
  review_status TEXT DEFAULT 'pending' 
    CHECK (review_status IN ('pending', 'approved', 'rejected')),
  reviewed_at TIMESTAMPTZ,
  reviewed_by TEXT,
  rejection_reason TEXT,
  rejection_feedback TEXT,

  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes
CREATE INDEX idx_cs_agent_decisions_review_status 
  ON raw_ops.cs_agent_decisions(review_status) 
  WHERE review_status = 'pending';

CREATE INDEX idx_cs_agent_decisions_ticket 
  ON raw_ops.cs_agent_decisions(ticket_id);

Tables (v3.6)

raw_ops.ticket_messages

CREATE TABLE raw_ops.ticket_messages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  ticket_id UUID NOT NULL REFERENCES raw_ops.support_tickets(id) ON DELETE CASCADE,
  direction TEXT NOT NULL CHECK (direction IN ('inbound', 'outbound')),
  sender TEXT NOT NULL,  -- customer email or agent persona
  subject TEXT,
  body TEXT NOT NULL,
  email_message_id TEXT,  -- For threading reference
  delivery_id TEXT,       -- Customer.io delivery ID for outbound
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_ticket_messages_ticket_id ON raw_ops.ticket_messages(ticket_id);

Ticket Statuses

Status Description
pending New ticket, not yet viewed
in_progress Ticket viewed, being worked on
awaiting_reply Outbound ticket waiting for customer response
resolved Issue resolved, response sent
closed Ticket closed without response

Key Views

  • v_pack_queue_with_batch - Pack Day queue with batch traceability
  • v_inventory_summary - Current inventory levels
  • v_order_fulfillment_status - Order status tracking
  • v_support_queue - Open tickets with customer context, message count (v3.6)

Edge Functions

calculate-pack-day-instructions

Purpose: Calculate PCM requirements based on weather forecasts

Trigger: Portal Pack Day button

Process: 1. Fetch orders with export_state = 'sent' 2. Get 48-hour weather forecast per postcode 3. Calculate PCM pack count based on temperature 4. Create packing instruction records

send-support-email (v3.6)

Purpose: Send support responses via Customer.io with email threading

Trigger: Support Workstation "Send Response" button or CS-03 Agent

Process: 1. Receive ticket ID, customer email, subject, message 2. Fetch email_message_id from ticket for threading 3. Build headers: In-Reply-To + References if reply 4. Prepend [Support] to subject if not present 5. Select random persona (Sophie/Tom/Lucy) 6. Send via Customer.io Transactional API 7. Insert message into ticket_messages (direction: outbound) 8. Update ticket status to resolved 9. Return success with delivery_id and persona

Payload:

{
  "ticket_id": "uuid",
  "customer_id": "uuid",
  "customer_email": "customer@example.com",
  "subject": "Re: Original subject",
  "message": "Response body text",
  "source": "ops_portal | cs_agent",
  "delay_send": false
}

Response:

{
  "success": true,
  "delivery_id": "abc123",
  "persona": "Sophie",
  "threading_enabled": true,
  "sent_immediately": true
}

support-send-response (deprecated)

Purpose: Legacy email send function - replaced by send-support-email in v3.6


Make.com Webhooks

Portal Subscription Actions

Webhook URL: https://hook.eu2.make.com/25vurnbyc8bhvqi5zqvgf9mjf809z0m8

Scenario: "Portal Subscription Actions"

Routes:

Route Filter Action
Outbound Email action=outbound_email Customer.io transactional email
Ops Portal Frequency source=ops_portal, action=frequency_change Seal API edit intervalCount
Ops Portal Box Change source=ops_portal, action=box_change Seal API edit variantId
Ops Portal Skip source=ops_portal, action=skip Seal API skip billing attempt
Ops Portal Bypass source=ops_portal, action=pause/resume Seal API pause/resume

Outbound Email Route

Filter: {{1.action}} equals outbound_email

Payload:

{
  "action": "outbound_email",
  "ticket_id": "uuid",
  "customer_email": "customer@example.com",
  "customer_name": "John Smith",
  "subject": "Quick update about your delivery",
  "message": "Hi John, just wanted to let you know...",
  "reason": "Delivery Issue"
}

Customer.io API Call:

{
  "transactional_message_id": "support_response",
  "to": "{{1.customer_email}}",
  "identifiers": { "email": "{{1.customer_email}}" },
  "message_data": {
    "customer_name": "{{1.customer_name}}",
    "response_body": "{{1.message}}",
    "subject": "{{1.subject}}"
  },
  "from": "support@protocolraw.co.uk",
  "reply_to": "support@protocolraw.co.uk",
  "subject": "{{1.subject}}"
}


System Architecture

Database Architecture

Schema: raw_ops

Key Tables: - batches - Production batch tracking - orders - Customer order records - shipments - Dispatch and tracking data - packing_instructions - Pack Day PCM requirements - pack_quality_checks - Quality control metrics - temperature_logger_data - Post-delivery temperature audits - formulations - Recipe management - config - System configuration - cs_agent_decisions - AI agent decisions (v3.5) - ticket_messages - Conversation threads (v3.6)

Key Views: - v_pack_queue_with_batch - Pack Day queue with batch traceability - v_inventory_summary - Current inventory levels - v_order_fulfillment_status - Order status tracking - v_support_queue - Open tickets with message count (v3.6)

Access Levels: - Anon Key: Public read access + specific Edge Functions - Service Role Key: Full CRUD access (portal only, protected by Cloudflare Access)

API Integrations

Supabase REST API: - Base URL: https://znfjpllsiuyezqlneqzr.supabase.co/rest/v1/ - Authentication: Service Role Key in Authorization header - Schema: Accept-Profile: raw_ops

Supabase Edge Functions: - Base URL: https://znfjpllsiuyezqlneqzr.supabase.co/functions/v1/ - Authentication: Anon Key in Authorization header - Functions: calculate-pack-day-instructions, send-support-email

Supabase Storage: - Base URL: https://znfjpllsiuyezqlneqzr.supabase.co/storage/v1/ - Authentication: Service Role Key in Authorization header - Bucket: dispatch-files

External Dependencies

Google Fonts API: - Montserrat: fonts.googleapis.com/css2?family=Montserrat:wght@700 - Inter: fonts.googleapis.com/css2?family=Inter:wght@400;500;600

OpenWeatherMap API (via Edge Function): - Used for Pack Day weather forecasts - 1000 free calls/day - API key stored in Edge Function (not in portal)

Performance

Load Times: - Initial page load: <1 second - Tab switching: <50ms (instant) - Database queries: <100ms (indexed) - File uploads: ~1-2 seconds (depends on batch size) - Conversation thread load: <100ms (v3.6)

Scalability: - Built for 100,000+ customers - No hardcoded limits - Database indexed for performance - CDN-cached static assets


Security & Best Practices

Authentication & Authorization

Cloudflare Access Protection: - Portal protected by Google OAuth - Only authorized emails can access - 24-hour session expiry - Force re-authentication on sensitive operations

API Keys in Portal:

⚠ï¸Â Service Role Key is embedded in the HTML file

This key has full database access. Security measures:

  1. Protected by Cloudflare Access - Key only accessible to authenticated users
  2. HTTPS Only - All traffic encrypted in transit
  3. No public exposure - Portal URL not publicly listed
  4. Audit trail - All database operations logged
  5. Key rotation - Rotate key quarterly

Never: - Disable Cloudflare Access - Share portal URL publicly - Share login credentials - Access portal from untrusted devices - Take screenshots with sensitive data

Data Security

In Transit: - All connections use HTTPS/TLS 1.3 - API calls encrypted end-to-end - No unencrypted data transmission

At Rest: - Supabase database encrypted at rest - Storage files encrypted at rest - Automatic backups encrypted

Access Control: - Row-level security on sensitive tables - Service role key isolated to portal only - Edge Functions use limited anon key

Best Practices

Daily Operations: 1. Always log out after pack day (or let session expire) 2. Clear browser cache on shared devices 3. Never save passwords in browser on shared devices 4. Use private/incognito mode on shared devices 5. Report suspicious activity immediately

Batch Processing: 1. Double-check Order IDs before adding to batch 2. Review complete batch before submitting 3. Verify success message after submission 4. Keep paper backup of tracking numbers (Phase A) 5. Check Make.com processed successfully

Quality Control: 1. Verify PCM calculations match expected values 2. Cross-check pack queue against orders received 3. Monitor batch status transitions (QA_HOLD → RELEASED) 4. Review Recent Activity regularly for anomalies 5. Investigate any failed operations immediately

Incident Response: 1. Note exact error message and time 2. Check browser console for detailed errors (F12) 3. Check Supabase logs (Edge Functions, Storage, Database) 4. Document steps to reproduce 5. Check Make.com scenario execution history 6. Contact technical lead with details

Compliance

Data Protection: - Customer data (names, addresses) visible in portal - Covered by Protocol Raw privacy policy - GDPR compliance maintained - Data retention per policy

Food Safety: - Batch traceability maintained - Lab certificate links stored - Recall capability via batch_code - Quality check records preserved

Audit Trail: - All database operations timestamped - Created_at, updated_at on all records - ops_events log for critical operations - CSV files archived in Supabase Storage - Email conversation threads preserved (v3.6)


Troubleshooting

Common Issues

Portal Shows 404

Symptoms: Page not found error at ops.protocolraw.co.uk

Causes: 1. File not named index.html 2. File in wrong directory/folder 3. Deployment failed 4. DNS not propagated

Resolution: 1. Check Cloudflare Pages deployment status 2. Verify file is named exactly index.html 3. Check file is at root level (not in subfolder) 4. Try backup URL: protocol-raw-ops.pages.dev 5. Clear browser cache and retry

Can't Log In

Symptoms: Cloudflare Access blocks login

Causes: 1. Email not in authorized list 2. Session expired 3. Cookies blocked 4. Access policy changed

Resolution: 1. Verify email is in Cloudflare Access policy 2. Clear browser cookies for ops.protocolraw.co.uk 3. Try different browser 4. Check Cloudflare Access application is active 5. Contact admin to verify email whitelist

Pack Day Button Doesn't Work

Symptoms: Calculate PCM button shows error or nothing happens

Causes: 1. Edge Function not deployed 2. Edge Function error 3. No orders in sent state 4. Network connectivity issue 5. API quota exceeded

Resolution: 1. Open browser console (F12) and check for errors 2. Verify Edge Function exists: Supabase Dashboard → Edge Functions 3. Check Edge Function logs for errors 4. Query database: SELECT * FROM raw_ops.orders WHERE export_state = 'sent' 5. Check OpenWeatherMap API quota 6. Test Edge Function directly via Supabase dashboard

Fulfillment Upload Fails

Symptoms: Submit Batch shows error, CSV not uploaded

Causes: 1. Storage bucket doesn't exist 2. Service Role Key invalid 3. Network error 4. Bucket permissions incorrect

Resolution: 1. Check browser console for detailed error 2. Verify bucket exists: Supabase Dashboard → Storage → dispatch-files 3. Verify inbox/ folder exists in bucket 4. Check Service Role Key is correct 5. Test upload manually via Supabase Storage UI 6. Check bucket policies allow service role write access

Batch Creation Fails

Symptoms: Create Batch shows error, batch not in database

Causes: 1. Duplicate batch code 2. Service Role Key invalid 3. Table doesn't exist 4. Validation error 5. Database permissions

Resolution: 1. Check browser console for detailed error 2. Query database: SELECT * FROM raw_ops.batches WHERE batch_code = 'YOUR-CODE' 3. Verify table exists: Supabase Dashboard → Table Editor 4. Check all required fields are filled 5. Verify Service Role Key has INSERT permission 6. Try different batch code

LocalStorage Batch Lost

Symptoms: Current Batch empty after page refresh

Causes: 1. Browser cache cleared 2. Incognito/private browsing mode 3. Different browser/device 4. LocalStorage quota exceeded

Resolution: 1. Check you're using same browser as when batch was created 2. Don't use incognito/private mode during pack day 3. Re-add orders to batch (check Recent Activity for order IDs) 4. Submit batches more frequently to avoid loss 5. Keep paper backup of Order IDs during pack day (Phase A)

Customer Search Not Working

Symptoms: Search returns no results or errors

Causes: 1. RPC function missing 2. Database permissions 3. Network error

Resolution: 1. Check public.search_customers function exists 2. Verify function has EXECUTE granted to anon and service_role 3. Check browser console for RPC errors

Ticket Assignment Not Showing

Symptoms: Assignment dropdown empty or not visible

Causes: 1. No active agents in database 2. RPC function missing 3. Column missing on tickets table

Resolution: 1. Verify raw_ops.agents table has active agents 2. Check public.get_active_agents function exists 3. Verify assigned_to column exists on support_tickets

Outbound Tickets Not Sending Email

Symptoms: Ticket created but no email sent

Causes: 1. Make.com route missing 2. Customer.io template issue 3. Webhook error

Resolution: 1. Check Make.com scenario has outbound_email route 2. Verify Customer.io support_response template has {{subject}} in subject line 3. Check Make.com execution history for errors 4. Verify Customer.io API key is correct

Outbound Tickets Not Appearing in Queue

Symptoms: Created outbound ticket not visible

Causes: 1. Missing columns on tickets table 2. View not updated 3. Wrong status

Resolution: 1. Verify is_outbound and outbound_reason columns exist on support_tickets 2. Check public.v_support_queue view includes outbound fields 3. Verify ticket was created with status='awaiting_reply'

Agent Review Queue Not Loading

Symptoms: Agent Review tab shows no decisions or errors

Causes: 1. Table cs_agent_decisions doesn't exist 2. No pending decisions 3. RPC function missing

Resolution: 1. Verify raw_ops.cs_agent_decisions table exists 2. Check for pending decisions: SELECT * FROM raw_ops.cs_agent_decisions WHERE review_status = 'pending' 3. Verify module is correctly imported in app.js

Conversation Thread Not Loading (v3.6)

Symptoms: Thread panel shows "No messages" or errors

Causes: 1. Table ticket_messages doesn't exist 2. No messages for ticket 3. RPC function missing

Resolution: 1. Verify raw_ops.ticket_messages table exists 2. Check for messages: SELECT * FROM raw_ops.ticket_messages WHERE ticket_id = 'uuid' 3. Verify public.get_ticket_messages function exists 4. Check browser console for RPC errors

Email Not Threading (v3.6)

Symptoms: Customer sees separate emails instead of thread

Causes: 1. email_message_id not captured on ticket 2. Headers not sent 3. Customer.io template issue

Resolution: 1. Check ticket has email_message_id populated 2. Verify send-support-email Edge Function is deployed 3. Check Edge Function logs for header construction 4. Verify Customer.io template accepts headers parameter

Error Messages

"Failed to fetch" - Network connectivity issue - API endpoint unreachable - CORS error - Check internet connection - Try different network - Check Supabase status: status.supabase.com

"Authorization header required" - API key missing or invalid - Check configuration section of HTML - Verify Anon Key or Service Role Key is correct - Check key hasn't been rotated

"Permission denied" - Database RLS (Row Level Security) blocking operation - Service Role Key should bypass RLS - Check key is correct service role key (not anon key) - Verify key has correct permissions in Supabase

"Unique constraint violation" - Trying to create duplicate record - Common with batch codes - Check existing records - Use different identifier

"Network request failed" - DNS resolution issue - SSL certificate issue - Cloudflare error - Check Cloudflare status: cloudflarestatus.com - Try backup URL: protocol-raw-ops.pages.dev

Getting Help

Self-Service: 1. Check this troubleshooting section 2. Open browser console (F12) for detailed errors 3. Check Supabase logs (Edge Functions, Database, Storage) 4. Check Make.com scenario execution history 5. Query database directly via Supabase SQL Editor

Escalation: 1. Document exact error message 2. Note time of occurrence 3. Describe steps to reproduce 4. Check if issue is persistent or intermittent 5. Gather relevant screenshots (redact sensitive data) 6. Contact technical lead with details


Maintenance & Updates

Regular Maintenance

Daily (Pack Days): - [ ] Verify portal loads correctly - [ ] Test Pack Day calculation - [ ] Monitor fulfillment batch submissions - [ ] Check Make.com processed dispatches

Weekly: - [ ] Review Recent Batches for status changes - [ ] Check Recent Orders matches expected volume - [ ] Monitor browser console for JavaScript errors - [ ] Verify Cloudflare Access policy is current - [ ] Review Agent Review queue (clear any backlog)

Monthly: - [ ] Review Supabase API usage and quotas - [ ] Check OpenWeatherMap API call count - [ ] Audit user access list (add/remove as needed) - [ ] Review error logs for patterns - [ ] Test disaster recovery procedures - [ ] Review AI agent approval rates

Quarterly: - [ ] Rotate Supabase Service Role Key - [ ] Update portal with new key - [ ] Review and update user access policies - [ ] Performance audit and optimization - [ ] Documentation review and updates

Updating the Portal

To update portal files: 1. Make changes to local files 2. Push to GitHub repository 3. Cloudflare Pages auto-deploys from main branch 4. Verify changes at ops.protocolraw.co.uk 5. Test all functionality 6. Document changes in version history

Cache Busting

When updating JavaScript modules, bump the version parameter:

  1. In app.js: import support from './modules/support.js?v=N';
  2. In index.html: <script type="module" src="js/app.js?v=N"></script>

Current version: v18 (bumped for v3.8)

Deployment Checklist

  1. Update module files
  2. Bump version in app.js imports
  3. Bump version in index.html script tag
  4. Push to GitHub
  5. Cloudflare Pages auto-deploys
  6. Hard refresh browser (Ctrl+Shift+R)

Keyboard Shortcuts

Global

Shortcut Action
Tab Navigate between form fields
Enter Submit focused form
Escape Close modals
Ctrl/Cmd + F Find on page
Ctrl/Cmd + 1 Switch to Pack Day
Ctrl/Cmd + 2 Switch to Fulfillment
Ctrl/Cmd + 3 Switch to Batches
Ctrl/Cmd + 4 Switch to Inventory
Ctrl/Cmd + 5 Switch to Support
Ctrl/Cmd + 6 Switch to Agent Review
Ctrl/Cmd + 7 Switch to Live Chat

Support Workstation

Shortcut Action
Ctrl/Cmd + Enter Send response
Escape Deselect ticket / Close modal

Agent Review

Shortcut Action
A Approve selected decision
R Reject selected decision (opens feedback)
N Next decision in queue

Deployment

Cloudflare Pages

Project: protocol-raw-ops
Domain: ops.protocolraw.co.uk
Backup: protocol-raw-ops.pages.dev

Auto-Deploy: - Connects to GitHub repository - Deploys on push to main branch - Build command: (none - static files) - Output directory: /

Manual Deployment (if needed)

  1. Download current files from repository
  2. Make necessary changes
  3. Test locally (open in browser)
  4. Cloudflare Pages → Create deployment → Upload assets
  5. Verify at ops.protocolraw.co.uk

Appendix

Browser Compatibility

Fully Supported: - Chrome 90+ - Firefox 88+ - Safari 14+ - Edge 90+

Mobile: - iOS Safari 14+ - Chrome Android 90+

Not Supported: - Internet Explorer (any version) - Opera Mini

File Size & Performance

  • HTML file size: ~35 KB
  • JavaScript modules: ~65 KB total (including agent-review.js, thread updates)
  • CSS files: ~15 KB total (including agent-review.css)
  • No external JavaScript dependencies
  • Google Fonts: ~50 KB (cached)
  • Total page weight: <165 KB
  • Load time: <1 second on 3G

Data Retention

LocalStorage: - Fulfillment batch: Until submitted or manually cleared - Browser-specific: Not synced across devices

Database: - Orders: Indefinite (business records) - Batches: Indefinite (traceability requirement) - Shipments: Indefinite (delivery records) - Packing instructions: 24 months (archived after) - Quality checks: 24 months (regulatory requirement) - Support tickets: Indefinite (customer service records) - Ticket messages: Indefinite (conversation history, v3.6) - AI agent decisions: 12 months (training data, then anonymized)

Backup & Recovery

Cloudflare Pages: - All deployments preserved - Can rollback to previous version instantly - Deployment history maintained indefinitely

Supabase Database: - Automatic daily backups - Point-in-time recovery (7 days) - Manual backup via Supabase dashboard

Critical Data: - Export batches table monthly (CSV) - Export orders table monthly (CSV) - Store batch lab certificates in cloud storage - Maintain paper backup of critical batch codes

Support Contacts

Portal Issues: - Cloudflare Support: https://support.cloudflare.com - Supabase Support: https://supabase.com/support

System Status: - Cloudflare: https://cloudflarestatus.com - Supabase: https://status.supabase.com - OpenWeatherMap: https://status.openweathermap.org

Internal: - Operations Lead: [contact info] - Technical Lead: [contact info] - QA Lead: [contact info]


For LLM Assistants

Adding New Features to Support Workstation

  1. Add RPC function in Supabase (public schema)
  2. Grant EXECUTE to service_role and anon
  3. Add JavaScript function in support.js
  4. Export function in default export
  5. Expose in app.js under window.opsPortal.support
  6. Add UI elements in index.html with onclick handlers
  7. Bump version numbers and deploy

Key File Locations

  • HTML: index.html (all tab sections)
  • JavaScript: js/modules/*.js (tab-specific logic)
  • CSS: css/*.css (component styles)
  • Global API: js/app.js (window.opsPortal.*)
  • Database: Supabase raw_ops schema + public RPC functions

Common Patterns

Adding a new tab module: 1. Create js/modules/newfeature.js 2. Create css/newfeature.css (if needed) 3. Import in app.js 4. Add to window.opsPortal namespace 5. Add tab HTML in index.html 6. Add navigation button

Adding database functionality: 1. Create table/view in Supabase 2. Create RPC function if complex logic needed 3. Add API call in api.js 4. Expose via module function 5. Wire up to UI

v3.6 File Changes Summary

File Change
index.html Added conversation thread panel, editable subject field
js/app.js Updated support module integration for threading
js/config.js Bumped to v3.6
js/modules/support.js Added thread display, subject editing, send-support-email call
Edge Function send-support-email New unified email sending with threading

v3.5 File Changes Summary

File Change
index.html Added Agent Review tab + content section
js/app.js Added agentReview module import and registration
js/config.js Fixed syntax error, bumped to v3.5
js/modules/agent-review.js New file
js/modules/support.js Fixed order_number_label reference
css/agent-review.css New file

Version History

Version Date Changes
3.8 2026-02-17 Batch status terminology corrected (CLEARED→RELEASED, QA_FAIL→QUARANTINE), database schema updated to match live system, documentation aligned to portal v3.8
3.7 2026-02 Refund action buttons in Support Workstation
3.6 2026-01-20 Conversation thread panel, email threading (In-Reply-To/References), [Support] subject prefix, editable subject, ticket_messages table, send-support-email Edge Function
3.5 2026-01-19 Agent Review tab for shadow mode validation, bug fixes
3.4 2026-01-13 Documentation consolidation - merged v2.0 and v3.3 into comprehensive document
3.3 2025-12-10 Customer Search, Customer Profile, Ticket Assignment, Outbound Tickets
3.2 2025-12-10 Subscription actions, order history, internal notes, bidirectional Seal sync
3.1 2025-12-09 Support Workstation UI refinements
3.0 2025-12-04 Modular ES6 architecture, Support Workstation
2.1 2025-11-28 Auto-generated batch codes, dual ID system
2.0 2025-11-15 Pack Day, Fulfillment, Batch Creation, tab navigation, Protocol Raw branding
1.0 2025-11-04 Initial basic fulfillment interface

Document Owner: Protocol Raw Operations Team
Last Reviewed: February 17, 2026
Next Review: May 17, 2026
Version: 3.8
Status: Production Ready ✅


End of Protocol Raw Operations Portal Documentation v3.8