Skip to content

SOP-PROOF-01: Proof Portal System

Document Status: PRODUCTION READY
Version: 1.0
Last Updated: 2026-01-27
Owner: Operations
Classification: Internal Reference


1. Overview

1.1 Purpose

The Proof Portal is Protocol Raw's public-facing verification system. It allows customers, vets, and prospects to verify the safety testing results for any released batch. The portal serves as the operational manifestation of "Proof, Not Promises" — transforming trust claims into verifiable evidence.

1.2 Strategic Role

The Proof Portal is not a marketing asset. It is:

  1. A verification tool — Customers confirm their batch passed testing
  2. A trust-transfer mechanism — Customers share proof with vets and family
  3. A competitive moat — Time-locked trust capital that compounds with each batch
  4. A differentiation signal — Proves Protocol Raw operates differently than competitors

1.3 Access Points

Entry Point URL Use Case
QR Code Scan proof.protocolraw.co.uk/batch/{id} Customer scans packaging
Portal Homepage proof.protocolraw.co.uk Manual batch search
Shared Link proof.protocolraw.co.uk/batch/{id} Vet/family receives link
Website Link protocolraw.co.uk/proof Prospect exploration

2. System Architecture

2.1 Technical Stack

Component Technology Purpose
Edge Function Supabase Deno Server-side rendering, API endpoints
Database Supabase PostgreSQL Batch data, lab results, analytics
Domain Cloudflare DNS, SSL, caching
Fonts Google Fonts Inter, Montserrat

2.2 Edge Function: proof-page

Location: Supabase Edge Functions
Schema: raw_ops

Routes

Route Method Handler Purpose
/ GET servePortalHomepage() Portal homepage with search
/batch GET servePortalHomepage() Alias for homepage
/batch/{public_batch_id} GET serveBatchProofPage() Individual batch page
/api/search GET handleBatchSearch() Batch lookup API
/api/event POST handleEventTracking() Analytics events
?batch={id} GET Legacy redirect Backwards compatibility

2.3 Database Schema

View: v_released_batches_verified

Source view that aggregates batch and lab result data for released batches only.

Column Type Description
id UUID Batch primary key
batch_code TEXT Human-readable batch ID (e.g., TEST-BATCH-004)
public_batch_id TEXT URL-safe identifier (e.g., PR-2CCF573F)
product_id UUID FK to products table
formulation_id UUID FK to formulations table
produced_at TIMESTAMPTZ Production date
reported_at TIMESTAMPTZ Lab report date
salmonella_absent BOOLEAN TRUE = not detected
listeria_absent BOOLEAN TRUE = not detected
enterobacteriaceae_cfu_per_g INTEGER Colony count
lab_name TEXT Testing laboratory name
report_pdf_url TEXT Link to original certificate

Table: proof_page_events

Analytics tracking for proof portal engagement.

CREATE TABLE raw_ops.proof_page_events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type TEXT NOT NULL,
  batch_code TEXT NOT NULL,
  public_batch_id TEXT,
  referrer TEXT,
  user_agent TEXT,
  device_type TEXT,
  share_method TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Indexes
CREATE INDEX idx_proof_page_events_type_created 
  ON raw_ops.proof_page_events(event_type, created_at DESC);
CREATE INDEX idx_proof_page_events_batch 
  ON raw_ops.proof_page_events(batch_code, created_at DESC);

-- Permissions
GRANT INSERT, SELECT ON raw_ops.proof_page_events TO anon;

3. Portal Homepage

3.1 URL

https://proof.protocolraw.co.uk/

3.2 Purpose

Allow customers to manually search for their batch if they don't have the direct QR link.

3.3 Page Structure

Hero Section (Stone background + dot texture)

Headline:

Proof, Not Promises

Subtitle:

This is our searchable database of independent test results. Enter the Batch ID found on your packaging to view its safety certificate.

Search Card: - Input field: Accepts batch ID (auto-uppercases) - Button: "Verify Batch" - States: Default → Loading → Success/Error/Info - Hint: "Find your Batch ID on the QR code label"

Latest Batch Showcase: - Displays most recent released batch - Shows test results summary - Links to full batch page

Process Timeline Section (Cream background)

Headline:

Hold-and-Release Process

Subtitle:

How every batch moves from us to your kitchen

Steps: 1. Batch Produced — Food blended, portioned, frozen with unique ID 2. Independent Testing — Sample sent to UKAS-accredited lab 3. Results Received — Lab tests for Salmonella, Listeria, hygiene indicators 4. Verified & Released — Only passing batches ship

CTA Section (Stone background + dot texture)

Headline:

Start Your Dog's Plan

Body:

Use our calculator to determine your dog's portion, then subscribe to receive verified batches.

CTAs: - Primary: "Calculate Portion" → /calculator - Secondary: "How It Works" → /how-it-works

3.4 Search API

Endpoint: GET /api/search?batch_code={id}

Response:

// Found and released
{ "found": true, "status": "RELEASED", "public_batch_id": "PR-2CCF573F" }

// Found but not released
{ "found": true, "status": "NOT_RELEASED" }

// Not found
{ "found": false }


4. Individual Batch Page

4.1 URL

https://proof.protocolraw.co.uk/batch/{public_batch_id}

4.2 Purpose

Display complete verification data for a specific batch. This is the page customers land on from QR codes and shared links.

4.3 Page Structure

Header Section

Brand:

Protocol Raw

Badge:

✓ Verified Safe
(Forest Green, animated glow after reveal)

Headline:

This Batch is Verified Safe.

Intro Copy:

This is your proof that your dog's food is safe. This batch was tested for Salmonella, Listeria, and Enterobacteriaceae before release.

You can feed your dog with confidence.

Batch Info Card: | Label | Value | |-------|-------| | BATCH ID | {batch_code} | | TESTED | {reported_at} | | STATUS | Verified Safe |

Share Actions: - "Send to My Vet" (Forest Green button) - "Share This Proof" (White outline button) - Hint: "Useful if your vet or family has questions."

Lab Analysis Section

Heading:

Independent Lab Analysis

Test Card 1: Salmonella | Field | Value | |-------|-------| | Test Name | Pathogen Test: Salmonella | | Badge | PASS (green circle) | | Result | Absent in 25g | | Standard | Absent in 25g | | Explanation | No Salmonella was detected in the tested sample. |

Test Card 2: Listeria | Field | Value | |-------|-------| | Test Name | Pathogen Test: Listeria monocytogenes | | Badge | PASS/PENDING/FAIL | | Result | Absent in 25g | | Standard | Absent in 25g | | Explanation | No Listeria was detected in the tested sample. |

Test Card 3: Enterobacteriaceae | Field | Value | |-------|-------| | Test Name | Hygiene Indicator: Enterobacteriaceae | | Badge | PASS/PENDING | | Result | {value} cfu/g | | Standard | <100 cfu/g | | Explanation | Low hygiene indicator confirms good manufacturing and cold-chain control. |

Safe and Complete Section

Heading:

Safe and Complete

Body:

Formulated to meet FEDIAF nutritional guidelines for all life stages.

CTA: "View Formulation Analysis" (conditional, if URL exists)

Lab Certificate Section

Heading:

Original Lab Certificate

Body:

We translate the scientific report into the clear summary above. For complete transparency, you can also view the original certificate issued by the lab.

CTA: "Download Lab Certificate (PDF)" (conditional, if URL exists)

Background: Espresso with dot texture

Text:

Complete nutrition, verified safe.


5. Share Functionality

5.1 Strategic Purpose

Share buttons are a growth primitive, not a marketing feature. They enable customers to transfer trust to skeptical audiences (vets, family) using verifiable evidence rather than brand claims.

5.2 Send to My Vet

Trigger: Button click
Action: Opens mailto with prefilled content

Subject:

Independent pathogen test results - raw diet (Protocol Raw)

Body:

Hi,

I wanted to share the independent pathogen test results for the raw food I'm feeding my dog.

This batch was tested before release for:
- Salmonella
- Listeria
- Enterobacteriaceae (hygiene indicator)

The full lab report is published here:
{batch_url}

I'm sharing this because safety is the main concern I've heard raised around raw diets, and I found this level of transparency helpful when deciding what to feed.

Best regards

Design Rationale: - Clinical framing ("pathogen test results") speaks vet language - Bullet list is scannable - States why sharing without asking for validation - No marketing language

5.3 Share This Proof

Trigger: Button click
Action: Web Share API (mobile) or copy to clipboard (desktop)

Share Data:

{
  title: 'Batch {batch_code} Lab Results',
  text: 'My dog\'s food is tested for Salmonella and Listeria before it\'s released. You can see the lab results here:',
  url: '{batch_url}'
}

Design Rationale: - Names specific pathogens (not abstract "safety") - "Before it's released" = Hold-and-Release without explaining it - First person ("my dog's food") = non-preachy - No emojis, no "Verified Safe" badge text

5.4 Share Hint

Text:

Useful if your vet or family has questions.

Purpose: Frames sharing as practical, not promotional. Gives permission without pressure.


6. Analytics

6.1 Events Tracked

Event Trigger Properties
proof_page_view Page load batch_code, public_batch_id, referrer, device_type
send_to_vet_click Vet button clicked batch_code, share_method: 'email'
share_proof_click Share button clicked batch_code, share_method: 'native' or 'copy'

6.2 Tracking Implementation

function trackEvent(eventType, extraData = {}) {
  const payload = {
    event_type: eventType,
    batch_code: batchCode,
    public_batch_id: publicBatchId,
    referrer: document.referrer || null,
    device_type: window.innerWidth <= 768 ? 'mobile' : 'desktop',
    ...extraData
  };

  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/event', JSON.stringify(payload));
  } else {
    fetch('/api/event', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
      keepalive: true
    }).catch(() => {});
  }
}

6.3 Key Metrics

Metric Query Insight
Daily page views COUNT(*) WHERE event_type = 'proof_page_view' AND created_at > now() - interval '1 day' QR scan engagement
Share rate share_clicks / page_views Trust transfer activity
Vet share rate vet_clicks / page_views Professional validation seeking
Mobile vs Desktop GROUP BY device_type Access context
Referrer breakdown GROUP BY referrer Entry point analysis

6.4 Metabase Dashboard Queries

Daily Views:

SELECT 
  DATE(created_at) as date,
  COUNT(*) as views
FROM raw_ops.proof_page_events
WHERE event_type = 'proof_page_view'
GROUP BY DATE(created_at)
ORDER BY date DESC
LIMIT 30;

Share Funnel:

SELECT 
  event_type,
  COUNT(*) as count
FROM raw_ops.proof_page_events
WHERE created_at > now() - interval '30 days'
GROUP BY event_type
ORDER BY count DESC;

Share Method Breakdown:

SELECT 
  share_method,
  COUNT(*) as count
FROM raw_ops.proof_page_events
WHERE event_type = 'share_proof_click'
  AND created_at > now() - interval '30 days'
GROUP BY share_method;


7. Visual Design

7.1 Design System Reference

Source: Visual Identity Guide v2.2

7.2 Color Palette

Token Hex Usage
--col-espresso #2B2523 Primary text, footer
--col-forest-green #2D5144 Pass badges, verified status, share buttons
--col-burnt-sienna #B85C3A Primary CTAs
--col-cream #F9F7F4 Body background, header gradient
--col-warm-linen #EBE8E3 Card backgrounds, borders
--col-taupe #6B6360 Secondary text, labels
--col-amber #D4A574 Pending badges

Batch Page Container: Pure white (#FFFFFF) for clinical clarity

7.3 Typography

Element Font Weight Size (Desktop) Size (Mobile)
Brand header Montserrat 700 18px 18px
H1 Montserrat 700 48px 32px
H2 Montserrat 700 28px 22px
Body Inter 400 18px 16px
Labels Inter 600 10-11px 10-11px
Buttons Inter 600 14-15px 14px

7.4 Animations

All animations respect prefers-reduced-motion: reduce.

Element Animation Duration Delay
Container Fade up + scale 1000ms 100ms
Verified badge Bounce in 700ms 500ms
Badge glow Continuous pulse 3000ms After reveal
Test cards Staggered slide up 400ms 0/150/300ms
Progress rings Stroke draw 1200ms On viewport
Status dot Pulse 2000ms Continuous

7.5 Responsive Breakpoints

Breakpoint Changes
≤640px Single column layout, stacked share buttons, smaller headings
>640px 3-column batch info, side-by-side share buttons

8. Security

8.1 Content Security Policy

default-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
script-src 'self' 'unsafe-inline';
frame-ancestors 'none';
base-uri 'self';
form-action 'self'

8.2 Additional Headers

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()

8.3 Data Access

  • Only RELEASED batches are visible via v_released_batches_verified
  • Batches in testing/hold status return "NOT_RELEASED" with no details
  • No PII is exposed through the proof portal

9. Caching

Page Cache-Control Rationale
Batch pages public, max-age=3600, stale-while-revalidate=86400 Lab results don't change once released
Portal homepage public, max-age=300 Latest batch updates more frequently
API endpoints No cache Real-time data

10. Copy Principles

10.1 Voice Guidelines

Aligned with: Protocol Raw Brand Voice & Copy Guidelines v1.0

  • Show, don't tell — Let lab results speak
  • Systematic, not emotional — Facts over reassurance
  • Clinical where appropriate — Vets are an audience
  • No anxiety amplification — Don't mention risks at relief moments

10.2 Key Copy Decisions

Element Decision Rationale
Badge text "Verified Safe" not "Verified & Released" Customer language, not internal
Intro copy Names pathogens upfront Address the fear directly
Test explanations Single sentence, no "What this means:" Trust readers, don't over-explain
Listeria card No mention of illness severity Don't spike anxiety at relief moment
FEDIAF claim "Formulated to meet" not "independently verified" Only claim verification when certificate exists
Social share Names pathogens, mentions "before release" Specific, not abstract
Vet email Clinical tone, bullet points Vets want data, not marketing

11. Operational Procedures

11.1 Batch Release Flow

1. Batch produced → status = 'PRODUCED'
2. Sample sent to lab → status = 'TESTING'
3. Results received → lab_results record created
4. QC review → status = 'RELEASED' (if pass)
5. Proof page becomes accessible

11.2 Lab Result Integration

Lab results are parsed from PDF certificates using GPT-4o Vision and stored in lab_results table. See SOP-0X for lab result processing.

11.3 Monitoring

Alert Triggers: - Proof page error rate > 1% - API endpoint latency > 2s - Analytics insert failures

Dashboard: Metabase proof portal dashboard (to be created)


12. Future Enhancements

12.1 Planned

Enhancement Priority Status
Metabase dashboard High Pending
Download certificate tracking Medium Not started
Formulation link tracking Medium Not started
A/B test share copy Low Not started

12.2 Considered but Deferred

Enhancement Reason Deferred
Social proof counters Could feel like marketing
Comments/reviews Out of scope for verification tool
Batch comparison Low value, adds complexity

Document Relationship
Visual Identity Guide v2.2 Design system source
Protocol Raw Brand Voice Guidelines v1.0 Copy voice reference
SOP-0X Lab Result Processing Upstream data flow
SOP-01 Batch Lifecycle Batch status management

14. Version History

Version Date Author Changes
1.0 2026-01-27 Claude Initial comprehensive documentation

Appendix A: Complete Edge Function Route Handlers

A.1 servePortalHomepage()

Fetches latest released batch and renders homepage HTML.

A.2 serveBatchProofPage()

Fetches batch by public_batch_id from v_released_batches_verified, joins product and formulation data, renders batch page HTML.

A.3 handleBatchSearch()

Looks up batch by public_batch_id, returns JSON with found/status/public_batch_id.

A.4 handleEventTracking()

Validates event_type, inserts to proof_page_events table, returns success/error JSON.


Appendix B: Sample Analytics Queries

B.1 Weekly Share Report

SELECT 
  DATE_TRUNC('week', created_at) as week,
  COUNT(*) FILTER (WHERE event_type = 'proof_page_view') as views,
  COUNT(*) FILTER (WHERE event_type = 'send_to_vet_click') as vet_shares,
  COUNT(*) FILTER (WHERE event_type = 'share_proof_click') as social_shares,
  ROUND(
    COUNT(*) FILTER (WHERE event_type IN ('send_to_vet_click', 'share_proof_click'))::numeric / 
    NULLIF(COUNT(*) FILTER (WHERE event_type = 'proof_page_view'), 0) * 100, 
    2
  ) as share_rate_pct
FROM raw_ops.proof_page_events
WHERE created_at > now() - interval '12 weeks'
GROUP BY DATE_TRUNC('week', created_at)
ORDER BY week DESC;

B.2 Top Batches by Views

SELECT 
  batch_code,
  COUNT(*) as views,
  COUNT(*) FILTER (WHERE event_type = 'share_proof_click') as shares
FROM raw_ops.proof_page_events
WHERE event_type = 'proof_page_view'
  AND created_at > now() - interval '30 days'
GROUP BY batch_code
ORDER BY views DESC
LIMIT 10;