Skip to content

SOP CS-02: Live Chat System (v1.8)

Status: Production Ready
Last Updated: 2026-01-21
Owner: Protocol Raw Operations
Platform: Supabase Edge Functions + OpenAI GPT-4o-mini + Cloudflare Pages + Slack + Customer.io


Overview

AI-powered live chat widget for the Protocol Raw website that handles pre-purchase inquiries, provides product information, and escalates complex issues with full AI triage integration - generating draft responses and sending acknowledgment emails automatically.

Flow: Website Widget → Supabase Edge Function → Knowledge Base Fetch → OpenAI GPT-4o-mini (streaming) → AI Response (or Escalation → Email Validation → AI Triage → Acknowledgment Email → Slack)

Key Achievements: - 24/7 instant response to common questions - Automatic AI-drafted responses for escalated chats - Customer acknowledgment within seconds of escalation - Email validation ensures customer context is loaded - Human operators see tickets with ready-to-review drafts

v1.8 Changes: - Consolidated documentation from v1.6 and v1.7 - Restored missing Escalation Flow, Support Workstation, and Database Schema sections

v1.7 Changes: - Widget v6.7 - Launcher label shortened to "Ask a question" (fits mobile) - Mobile back button fix - Back button now closes chat instead of leaving site (History API) - Widget v6.6 - Opening message signals domain expertise (feeding amounts, transition timing, batch testing) - Header subtext - Updated to "Instant answers, 24/7"


Architecture Components

System Flow Diagram

+-----------------------------+
|   Website Visitor           |
|   (Chat Widget v6.7)        |
+-------------+---------------+
              | POST /functions/v1/chat
              v
+-----------------------------+
|  Supabase Edge Fn           | <- chat (v20)
|  (Deno Runtime)             |
+-------------+---------------+
              |
        +-----+-----+
        v           v
+-------------+ +---------------------+
| Knowledge   | |   Session           |
| Base        | |   CRUD              |
| Fetch       | |   (parallel)        |
+------+------+ +----------+----------+
       |                   |
       +-------+-----------+
               v
       +---------------------+
       |   OpenAI            |
       |   GPT-4o-mini       |
       |   (streaming)       |
       +----------+----------+
                  |
                  v
       +---------------------+
       |  AI Response        |
       |  (with judgment     |
       |   on escalation)    |
       +----------+----------+
                  |
            +-----+-----+
            v           v
      +----------+ +--------------------------+
      |  Normal  | | AI Chose to Escalate     |
      | Response | | (asks for email)         |
      +----+-----+ +----------+---------------+
           |                  |
           v                  v
    +------------+    +------------------+
    |  Rating    |    | Customer         |
    |  Buttons   |    | Provides Email   |
    +------------+    +--------+---------+
                               |
                               v
                      +------------------+
                      | Email Validation |
                      | (check customer  |
                      |  database)       |
                      +--------+---------+
                               |
                    +----------+----------+
                    v                     v
            +---------------+    +---------------+
            | Email Found   |    | Email NOT     |
            | -> Proceed    |    | Found -> Ask  |
            |               |    | for correct   |
            +-------+-------+    | email         |
                    |            +---------------+
                    v
          +---------------------+
          | Get Customer Context|
          | (subscription,      |
          |  delivery status)   |
          +----------+----------+
                     |
                     v
          +---------------------+
          | cs-agent-triage     |
          | (creates ticket +   |
          |  drafts response)   |
          +----------+----------+
                     |
           +---------+---------+
           v                   v
    +-------------+    +-------------+
    | Policy Gate |    | AI Draft    |
    | Triggered   |    | Generated   |
    | (human-only)|    | (ready to   |
    |             |    |  review)    |
    +------+------+    +------+------+
           |                  |
           +--------+---------+
                    v
          +---------------------+
          | send-support-email  |
          | (acknowledgment)    |
          +----------+----------+
                     |
                     v
    +-------------+ +-------------+ +------------------+
    | Customer    | | Slack       | | Support          |
    | Receives    | | Alert       | | Workstation      |
    | Ack Email   | | #cust-ops   | | (with draft)     |
    +-------------+ +-------------+ +------------------+

Chat Widget (v6.7)

Widget Positioning Strategy

The chat widget uses a two-stage positioning approach:

  1. Launcher button - Short, neutral, fits mobile: "Ask a question"
  2. Opening message - Signals domain expertise and specific capabilities

This separation ensures the launcher doesn't overwhelm mobile screens while the opening message immediately establishes credibility.

Widget Copy

Launcher button:

Ask a question

Header:

Protocol Raw
Instant answers, 24/7

Opening message (bot greeting):

I can help with feeding amounts, transition timing, ingredients, or how our batch safety testing works. What would you like to know?

Mobile UX Fixes (v6.7)

Back button handling: - Chat panel pushes a history state when opened - popstate listener closes chat instead of navigating away - Prevents visitors from accidentally leaving the site

// When chat opens
history.pushState({ prChatOpen: true }, '');

// Listen for back button
window.addEventListener('popstate', (e) => {
  if (state.isOpen) {
    close();
  }
});

Deployment

Location: https://cdn.protocolraw.co.uk/chat-widget.js

Version: 6.7

To deploy: 1. Upload chat-widget.js to Cloudflare Pages 2. Cache will update within minutes


Edge Function: chat (v20)

Deployment

URL: https://znfjpllsiuyezqlneqzr.supabase.co/functions/v1/chat

Runtime: Deno (Supabase Edge Functions)

Version: v20 (AI triage integration + email validation)

Key Features in v20

Email Validation: - When customer provides email, system checks v_customer_full_context - If email not found in customer database, AI asks for registered email - Prevents escalation with unknown emails (no customer context available)

Customer Context Lookup: - Fetches full customer profile before calling triage - Includes: subscription status, box size, delivery tracking, shipment status - Context passed to triage for better AI draft quality

AI Triage Integration: - Instead of creating ticket directly, calls cs-agent-triage - Triage creates ticket + generates AI draft response - Policy gates still apply (health concerns → human-only)

Acknowledgment Email: - Sent via send-support-email Edge Function (template mode) - Uses Customer.io template ID 5 - Sent immediately after triage creates ticket

Request Format

Method: POST

Headers:

Content-Type: application/json

Body:

{
  "message": "User's question here",
  "visitor_id": "v_abc123xyz",
  "session_id": "uuid-string",
  "entry_page": "/calculator",
  "referrer": "https://google.com",
  "device_type": "desktop"
}

Response Format (SSE Stream)

Content-Type: text/event-stream

1. Start Event:

event: start
data: {"session_id":"uuid-string"}

2. Chunk Events:

event: chunk
data: {"content":"Protocol"}

event: chunk
data: {"content":" Raw"}

3. Done Event:

event: done
data: {"session_id":"uuid","message_id":"uuid","escalated":false,"escalation_reason":null,"response_time_ms":847}

Environment Variables

Variable Description
OPENAI_API_KEY OpenAI API key for GPT-4o-mini
SUPABASE_URL Supabase project URL
SUPABASE_SERVICE_ROLE_KEY Service role key for database access
SLACK_CHAT_ESCALATION_WEBHOOK Webhook for #cust-ops channel
AGENT_INTERNAL_SECRET Secret for calling cs-agent-triage

AI Configuration

Model: GPT-4o-mini
Temperature: 0.3
Max Tokens: 400
Context Window: Last 20 messages
Streaming: Enabled


Escalation Flow

Overview

When AI decides to escalate, the system: 1. Validates the customer's email against the database 2. Fetches full customer context (subscription, delivery, etc.) 3. Calls AI triage to create ticket and draft response 4. Sends acknowledgment email to customer 5. Notifies team via Slack

Email Validation

Why: Without validated email, we can't load customer context, resulting in poor AI drafts.

Flow: 1. Customer provides email in chat 2. fn_link_chat_to_customer checks if email exists in customers table 3. If found → customer_id is set on session → proceed with escalation 4. If NOT found → AI asks for the registered email address

System Note Injection: When email is not found, the chat function injects:

[SYSTEM NOTE: The email "fake@example.com" was not found in our customer database. 
Ask the customer to provide the email address they used when they signed up for Protocol Raw.]

Customer Context

Before calling triage, the chat function fetches from v_customer_full_context:

Field Purpose
customer_id Identify customer
first_name Personalization
subscription_status Context for AI
box_size Order details
tracking_no Delivery context
shipment_status Delivery status
latest_event_text Latest courier update
next_billing_date Subscription timing
is_repeat_contacter Policy gate trigger
has_recent_credit Policy gate trigger

Triage Payload

The chat function sends this to cs-agent-triage:

{
  "customer_email": "customer@example.com",
  "subject": "Chat escalation",
  "body": "CUSTOMER DELIVERY STATUS:\nTracking: 1234567890\nStatus: IN_TRANSIT\nLatest Update: Out for delivery\n\nCHAT TRANSCRIPT:\nCustomer: My delivery was supposed to arrive yesterday...\nAI: I'd like our team to look at this...",
  "source": "chat",
  "customer_context": {
    "customer_id": "uuid",
    "first_name": "John",
    "subscription_status": "active",
    "box_size": "8kg",
    "is_repeat_contacter": false,
    "has_recent_credit": false
  }
}

Policy Gates

Triage applies the same policy gates as email:

Gate Code Trigger Result
health_vomiting "vomiting" in transcript Human-only, no draft
health_blood "blood" in transcript Human-only, no draft
repeat_contacter 3+ tickets in 7 days Human-only, no draft
has_recent_credit Recent credit + asking for more Human-only, no draft
financial_refund "refund" request Human-only, no draft

If no gate triggers, AI drafts a response using customer context.

Acknowledgment Email

Template ID: 5 (Customer.io)

Subject: We've received your message

Content:

Hi [first_name],

Thanks for chatting with us. I've flagged your concern to the team and someone will follow up by email within the next few hours.

If anything's urgent in the meantime, just reply to this email.

Protocol Raw

Timing: Sent immediately after triage creates ticket (no delay).


Support Workstation Integration

Badge Display

Chat escalation tickets show a distinctive badge in the queue:

💬 Escalated from chat

Styling: - Background: #E8F5E9 (light green) - Color: #4B685B (Protocol Raw green) - Font weight: 500

Ticket Details

When viewing a chat escalation ticket:

Field Value
Source chat
Channel badge 💬 Chat
Subject "Chat escalation"
Body Formatted transcript with delivery context
AI Draft Ready to review (if no policy gate)
Status escalated (if policy gate) or ai_reviewed

Conversation Thread

The ticket body contains the chat transcript:

CUSTOMER DELIVERY STATUS:
Tracking: 1234567890
Status: IN_TRANSIT
Latest Update: Out for delivery

CHAT TRANSCRIPT:
Customer: My delivery was supposed to arrive yesterday but it still hasn't come

AI: I'm sorry to hear that. I'd like our team to look at this properly. Could you share your email address?

Customer: john@example.com

AI: Thank you for sharing your email. I'll ensure our team follows up with you shortly.


Knowledge Base Integration

The chat system fetches its prompt from the centralised AI Knowledge Base (see SOP AI-KB-01 v1.8).

Sections Used (Chat Channel)

Section Purpose
brand_identity Voice, tone, terminology
response_principles 11 core principles
product_knowledge SKU, pricing, ingredients, FEDIAF
safety_verification Batch testing, proof portal
storage_serving Freezer, defrost, serve
feeding_calculations Reference examples, calculator link
transition_guidance 10-day schedule, warning signs
delivery_logistics DPD, dispatch days
self_service_portal Portal features
common_scenarios Shared Q&A
escalation_rules AI judgment rules
hard_boundaries Prohibitions
chat_context Chat-specific role
chat_format Short responses, markdown

Prompt Assembly

SELECT prompt, section_versions, token_estimate 
FROM raw_ops.fn_assemble_prompt('chat');

Rating System

How It Works

  1. After each AI response, thumbs up/down buttons appear on hover (always visible on mobile)
  2. Positive ratings submitted immediately
  3. Negative ratings prompt for optional feedback
  4. All ratings stored in chat_message_ratings table

Rating Endpoint

Edge Function: chat-rating

Request:

{
  "message_id": "uuid",
  "rating": "positive|negative",
  "feedback": "Optional text"
}

Monitoring Ratings

SELECT 
  rating,
  COUNT(*) as count,
  COUNT(feedback) as with_feedback
FROM raw_ops.chat_message_ratings
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY rating;

Database Schema

Table: chat_sessions

Column Type Description
id UUID Primary key (session_id)
visitor_id TEXT Persistent visitor identifier
visitor_email TEXT Email if provided for escalation
customer_id UUID Linked customer (if email validated)
entry_page TEXT First page visited
referrer TEXT Traffic source
device_type TEXT 'desktop' or 'mobile'
status TEXT 'active', 'escalated', 'closed'
support_ticket_id UUID Linked ticket if escalated
created_at TIMESTAMPTZ Session start
last_message_at TIMESTAMPTZ Last activity

Table: chat_messages

Column Type Description
id UUID Primary key (message_id)
session_id UUID FK to chat_sessions
role TEXT 'user' or 'assistant'
content TEXT Message text
tokens_used INTEGER Token count
response_time_ms INTEGER AI response latency
escalation_trigger BOOLEAN Did this response trigger escalation?
created_at TIMESTAMPTZ When sent

Table: chat_message_ratings

Column Type Description
id UUID Primary key
message_id UUID FK to chat_messages
rating TEXT 'positive' or 'negative'
feedback TEXT Optional feedback text
created_at TIMESTAMPTZ When rated

Database Schema Updates (v1.5)

chat_sessions - Added columns for triage integration:

Column Type Description
customer_id UUID FK to customers (set on email validation)
support_ticket_id UUID FK to support_tickets (from triage)

support_tickets - Source constraint updated:

CHECK (source IN ('email', 'chat', 'ops_portal'))

support_tickets - Status constraint updated:

CHECK (status IN ('new', 'open', 'in_progress', 'awaiting_reply', 'pending', 'resolved', 'closed', 'ai_reviewed', 'escalated'))

Function: fn_get_chat_history

Returns conversation history for a session:

SELECT * FROM raw_ops.fn_get_chat_history('session-uuid-here');
-- Returns: (id, session_id, role, content, created_at, escalation_trigger)

Updated to include escalation_trigger:

RETURNS TABLE (
  role TEXT,
  content TEXT,
  created_at TIMESTAMPTZ,
  escalation_trigger BOOLEAN
)

Escalation Detection

How AI Escalation is Detected

The edge function checks the AI's response for phrases indicating it chose to escalate:

const escalationPhrases = [
  'share your email',
  'provide your email',
  'your email address',
  'team to look at',
  'team to follow up',
  'someone will follow up',
  'someone will contact',
  'team will reach out',
  'have our team',
  'i\'d like our team'
];

Previous Escalation Check

The function also checks if a previous message in the session triggered escalation:

function checkPreviousEscalation(history) {
  return history.some(msg => 
    msg.role === 'assistant' && msg.escalation_trigger === true
  );
}

This ensures escalation proceeds when: 1. AI asks for email (escalation_trigger = true) 2. Customer provides email (no trigger in current response) 3. System detects previous escalation and proceeds


Operations Guide

Handling Chat Escalations

  1. Ticket appears in Support Workstation with "💬 Escalated from chat" badge
  2. Review AI draft (if available) - drafts use full customer context
  3. Check for policy gates - if triggered, no draft available
  4. Send response or edit draft before sending
  5. Response threads to acknowledgment email customer already received

Monitoring Escalation Quality

Check triage results:

SELECT 
  t.id,
  t.subject,
  t.ai_category,
  t.ai_confidence,
  t.ai_draft_response IS NOT NULL AS has_draft,
  d.policy_gate_triggered,
  d.policy_gate_code
FROM raw_ops.support_tickets t
LEFT JOIN raw_ops.cs_agent_decisions d ON d.ticket_id = t.id
WHERE t.source = 'chat'
ORDER BY t.created_at DESC
LIMIT 20;

Check email validation failures:

SELECT 
  id,
  visitor_email,
  customer_id,
  status
FROM raw_ops.chat_sessions
WHERE visitor_email IS NOT NULL
  AND customer_id IS NULL
  AND created_at > NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;

Daily Monitoring

  1. Check Ops Portal → Support Workstation for chat escalations
  2. Handle escalations within 4 hours (SLA target)
  3. Review any unmatched emails (customers providing wrong email)

Weekly Review

  1. Run policy gate analysis - are legitimate issues being blocked?
  2. Review AI draft quality for chat escalations
  3. Check email validation rate
  4. Update knowledge base if escalation rules need adjustment

Deployment

Edge Functions

# Chat function (v20)
supabase functions deploy chat --project-ref znfjpllsiuyezqlneqzr

# Send support email (for acknowledgments)
supabase functions deploy send-support-email --project-ref znfjpllsiuyezqlneqzr

# Triage function
supabase functions deploy cs-agent-triage --project-ref znfjpllsiuyezqlneqzr

# Rating function (no JWT verification)
supabase functions deploy chat-rating --no-verify-jwt --project-ref znfjpllsiuyezqlneqzr

Widget Deployment

# Upload to Cloudflare Pages
# File: chat-widget.js (v6.7)
# Path: https://cdn.protocolraw.co.uk/chat-widget.js

Customer.io Template

Template ID 5: Chat escalation acknowledgment - Must have "From" address configured - Variables: first_name


Troubleshooting

Escalation Not Creating Ticket

  1. Check email validation - is customer_id set on session?
  2. Check triage logs: supabase functions logs cs-agent-triage
  3. Verify AGENT_INTERNAL_SECRET is set

AI Draft Missing/Poor Quality

  1. Check if policy gate triggered (expected - no draft)
  2. Verify customer context is being fetched
  3. Check triage logs for errors

Acknowledgment Email Not Sent

  1. Check chat logs: supabase functions logs chat
  2. Verify send-support-email has JWT disabled
  3. Check Customer.io template ID 5 exists with "From" address

Customer Providing Unregistered Email

  1. AI should ask for registered email (check knowledge base escalation_rules)
  2. Customer may need to provide the email used at signup
  3. If customer is new (no account), escalation won't proceed - they should email support@protocolraw.co.uk

Email Threading Not Working

Acknowledgment email creates thread anchor. Human response should thread to it automatically via In-Reply-To headers.

Mobile Back Button Leaving Site

  1. Verify widget version is 6.7+
  2. Check popstate listener is registered
  3. Confirm history.pushState is called in open() function

Version History

v1.8 (2026-01-21) - Documentation Consolidation

Restored from v1.6: - Escalation Flow section (email validation, customer context, triage payload, policy gates, acknowledgment email) - Support Workstation Integration section (badge display, ticket details, conversation thread) - Database Schema Updates section (chat_sessions columns, support_tickets constraints, fn_get_chat_history)

Retained from v1.7: - Clean ASCII flow diagram - Widget v6.7 changes (launcher label, mobile back button fix) - All troubleshooting items

v1.7 (2026-01-21) - Widget UX Improvements

Widget Changes (v6.7): - Launcher label: "Feeding or safety questions?" → "Ask a question" (shorter, fits mobile) - Mobile back button now closes chat instead of leaving site (History API)

Widget Changes (v6.6): - Header subtext: "Usually replies instantly" → "Instant answers, 24/7" - Opening message updated to signal domain expertise: "I can help with feeding amounts, transition timing, ingredients, or how our batch safety testing works."

Rationale: Widget copy now mirrors visitor's internal state (feeding anxiety, safety concerns) while launcher stays short for mobile. Back button fix prevents accidental site abandonment.

v1.6 (2026-01-21) - Widget Positioning Update

Widget Changes (v6.6): - Widget label: "Questions?" → "Feeding or safety questions?" - Header subtext: "Usually replies instantly" → "Instant answers, 24/7" - Opening message updated to signal domain expertise

Rationale: Widget copy now mirrors visitor's internal state (feeding anxiety, safety concerns) and signals specific capabilities rather than generic chatbot positioning.

v1.5 (2026-01-21) - AI Triage Integration

New Features: - Escalated chats now go through cs-agent-triage - AI drafts generated with full customer context - Acknowledgment email sent immediately on escalation - Email validation before escalation (must be registered customer) - "Escalated from chat" badge in Support Workstation - Policy gates apply to chat escalations

Technical Changes: - Edge Function bumped to v20 - fn_get_chat_history updated to return escalation_trigger - support_tickets.source constraint updated for 'chat' - support_tickets.status constraint updated for 'ai_reviewed', 'escalated' - Customer.io template ID 5 for acknowledgment

Database: - chat_sessions.customer_id populated on email validation - chat_sessions.support_ticket_id populated from triage response

v1.4 (2026-01-12) - AI Judgment Escalation

  • Removed keyword-based escalation entirely
  • Edge Function bumped to v13
  • AI now uses judgment based on escalation_rules
  • Response principles consolidated (15 → 11)

v1.3 (2026-01-12) - Rating System

  • Added response rating system (thumbs up/down with optional feedback)
  • Edge Function v11 (returns message_id)
  • New Edge Function: chat-rating v1
  • Widget v6.1 (rating UI)

v1.2 (2026-01-12) - Knowledge Base Integration

  • Prompt content fetched from AI Knowledge Base
  • Edge Function v10

v1.1 (2026-01-12) - Streaming + Performance

  • Streaming SSE responses
  • GPT-4o-mini model
  • Parallelized DB calls

v1.0 (2025-12-04) - Initial Production Release

  • JSON request/response format
  • GPT-4o-mini integration
  • Basic escalation detection
  • Slack notifications

  • SOP CS-01 v2.1: AI Customer Service Triage (Email)
  • SOP CS-00 v1.5: Customer Operations System (architecture overview)
  • SOP AI-KB-01 v1.8: AI Knowledge Base (prompt management)
  • Ops Portal Documentation v3.7: Portal overview (Support Workstation updates)
  • Visual Identity Guide v2.1: Brand colors and styling

Document Owner: Protocol Raw Technical Team
Last Reviewed: 2026-01-21
Next Review: 2026-04-21
Version: 1.8
Status: Production Ready ✅