Skip to content

Database Schema

The OpenScouter database is a PostgreSQL schema managed by Supabase. It contains 29 tables across four functional domains: identity and access, study management, test data capture, and reporting. Row-Level Security is enabled on all tables.

Domain Overview

DomainTables
Identity and accessprofiles, tester_profiles, organizations, organization_members
Study managementjobs, job_testers, job_tasks, capacity_quiz_attempts, quiz_questions
Test data capturesessions, session_events, facial_snapshots, voice_segments, session_notes
Reportingreports, job_reports, report_findings, nd_stratification
Paymentspayouts, payout_line_items, stripe_connect_accounts

Core Tables

profiles

Extends the Supabase auth.users table with application-level data. One row per authenticated user.

ColumnTypeNotes
iduuidPrimary key. Foreign key to auth.users.id
emailtextUnique. Mirrors auth.users.email
full_nametextDisplay name
roleuser_roleEnum: business, tester, admin
organization_iduuidForeign key to organizations. Null for testers
created_attimestamptzSet on insert
updated_attimestamptzUpdated by trigger on any change

tester_profiles

Extended profile for users with role = tester. Contains neurodivergent category declarations and capability information.

ColumnTypeNotes
iduuidPrimary key. Foreign key to profiles.id
nd_categoriesnd_category[]Array of ND category enums. See enum types below
assistive_techtext[]List of assistive technologies used
biotextShort tester biography
capacity_verifiedbooleanTrue if quiz passed. Default false
capacity_scoreintegerScore from capacity quiz attempt
stripe_connect_idtextStripe Connect account ID for payouts
telegram_chat_idtextTelegram chat ID for notifications
availablebooleanWhether tester is accepting new studies
timezonetextIANA timezone string

jobs

A study commissioned by a business. One row per study.

ColumnTypeNotes
iduuidPrimary key
organization_iduuidForeign key to organizations
titletextStudy title
target_urltextURL to be tested
brieftextStudy instructions provided to testers
statusjob_statusEnum: draft, active, in_progress, completed, cancelled
nd_requirementsnd_category[]Required ND categories for tester matching
tester_countintegerNumber of testers required
compensation_gbpintegerPayment per tester in pence
tone_profiletone_profileEnum: standard, supportive, moderate, restorative
notes_confirmedbooleanTrue when all testers have confirmed findings. Gates Agent 3
stripe_payment_idtextStripe Checkout session ID
created_attimestamptz
updated_attimestamptz

sessions

One row per tester per study. Represents a single test run.

ColumnTypeNotes
iduuidPrimary key
job_iduuidForeign key to jobs
tester_iduuidForeign key to profiles
statussession_statusEnum: pending, active, completed, cancelled
test_tokentext48-char hex token for extension authentication
test_token_expires_attimestamptzToken TTL (24h from issuance)
test_token_revokedbooleanManual revocation flag
started_attimestamptzWhen tester clicked Start Test
ended_attimestamptzWhen tester clicked Stop Test
camera_consentbooleanTester granted camera access
microphone_consentbooleanTester granted microphone access
used_fallback_modelbooleanTrue if a fallback AI provider was used for any AI step
created_attimestamptz

session_events

Browser events captured by the Chrome extension during a test.

ColumnTypeNotes
iduuidPrimary key
session_iduuidForeign key to sessions
task_idtextTask identifier at time of event
event_typetextclick, scroll, focus, blur, keydown, etc.
timestampbigintUnix millisecond timestamp
xintegerPointer X coordinate
yintegerPointer Y coordinate
target_selectortextCSS selector of the target element
target_texttextVisible text of the target element
is_rage_clickbooleanThree or more clicks on same element within 500ms
time_on_element_msintegerPointer dwell time in milliseconds
scroll_depthnumericFractional page scroll depth at time of event
urltextPage URL at time of event
viewport_widthinteger
viewport_heightinteger

facial_snapshots

Facial expression data from camera captures during the test.

ColumnTypeNotes
iduuidPrimary key
session_iduuidForeign key to sessions
timestampbigintUnix millisecond timestamp
emotiontextEmotion label from DeepFace
confidencenumericDeepFace confidence score 0-1
face_detectedbooleanFalse if no face was found in the frame
action_unitsjsonbFACS Action Unit values from DeepFace
excludedbooleanTester-flagged for exclusion during cooldown

voice_segments

Transcribed voice segments from microphone capture.

ColumnTypeNotes
iduuidPrimary key
session_iduuidForeign key to sessions
task_idtextTask at time of segment
start_msbigintSegment start timestamp
end_msbigintSegment end timestamp
texttextTranscribed text
sentimenttextpositive, neutral, negative
keywordstext[]Extracted keywords
confidencenumericTranscription confidence 0-1
pii_detectedbooleanTrue if PII patterns found
processedbooleanFalse if PII pending sanitisation
excludedbooleanTester-flagged for exclusion

session_notes

AI-generated findings produced by Agent 2 (Analyst). Testers review and confirm these before reports are generated.

ColumnTypeNotes
iduuidPrimary key
session_iduuidForeign key to sessions
finding_texttextAI-generated barrier description
plain_englishtextPlain English summary
wcag_criteriontextWCAG success criterion code (e.g., 1.3.1)
wcag_leveltextA, AA, or AAA
severityfinding_severityEnum: critical, major, minor, advisory
confidencefinding_confidenceEnum: high, medium, low
bucket_metadatajsonb30-second bucket data for this finding
confirmedbooleanTester confirmed this finding. Default false
rejectedbooleanTester rejected this finding. Default false
tester_annotationtextOptional tester note
created_attimestamptz
updated_attimestamptz

reports

Individual tester reports produced by Agent 3 (Report Writer).

ColumnTypeNotes
iduuidPrimary key
session_iduuidForeign key to sessions
job_iduuidForeign key to jobs
tester_iduuidForeign key to profiles
report_texttextFull report content
tone_profiletone_profileTone profile used for this report
used_fallback_modelbooleanTrue if a fallback AI provider was used
generated_attimestamptzWhen Agent 3 completed

job_reports

Cross-test synthesis reports produced by Agent 4 (Synthesizer).

ColumnTypeNotes
iduuidPrimary key
job_iduuidForeign key to jobs
synthesis_texttextFull synthesized report
nd_stratificationjsonbFindings stratified by ND category
cross_tester_boostsjsonbBarriers boosted by 3+ tester corroboration
used_fallback_modelboolean
generated_attimestamptz

payouts

Payment records for tester compensation.

ColumnTypeNotes
iduuidPrimary key
tester_iduuidForeign key to profiles
job_iduuidForeign key to jobs
amount_gbpintegerAmount in pence
statuspayout_statusEnum: pending, processing, paid, failed
stripe_payout_idtextStripe payout object ID
paid_attimestamptzWhen Stripe confirmed delivery

quiz_questions and capacity_quiz_attempts

quiz_questions stores the accessibility knowledge questions used to verify tester capability. capacity_quiz_attempts records each tester’s quiz attempts.

ColumnTypeNotes
quiz_questions.iduuidPrimary key
quiz_questions.question_texttext
quiz_questions.nd_categorynd_categoryCategory this question tests
quiz_questions.correct_answertext
quiz_questions.difficultyinteger1-3 scale
capacity_quiz_attempts.tester_iduuidForeign key to profiles
capacity_quiz_attempts.scoreintegerQuestions answered correctly
capacity_quiz_attempts.passedbooleanTrue if score meets threshold
capacity_quiz_attempts.attempted_attimestamptz

Enum Types

nd_category

The 13 supported neurodivergent categories.

CREATE TYPE nd_category AS ENUM (
'autism',
'adhd',
'dyslexia',
'dyscalculia',
'dyspraxia',
'tourette_syndrome',
'hyperlexia',
'sensory_processing',
'irlen_syndrome',
'anxiety_disorder',
'ocd',
'acquired_brain_injury',
'other'
);

user_role

CREATE TYPE user_role AS ENUM ('business', 'tester', 'admin');

job_status

CREATE TYPE job_status AS ENUM (
'draft',
'active',
'in_progress',
'completed',
'cancelled'
);

session_status

CREATE TYPE session_status AS ENUM (
'pending',
'active',
'completed',
'cancelled'
);

finding_severity

CREATE TYPE finding_severity AS ENUM (
'critical',
'major',
'minor',
'advisory'
);

finding_confidence

CREATE TYPE finding_confidence AS ENUM ('high', 'medium', 'low');

tone_profile

CREATE TYPE tone_profile AS ENUM (
'standard',
'supportive',
'moderate',
'restorative'
);

payout_status

CREATE TYPE payout_status AS ENUM (
'pending',
'processing',
'paid',
'failed'
);

Row-Level Security Policy Examples

RLS is enabled on all 29 tables. Below are representative policy examples for the most commonly accessed tables.

profiles

Users can read their own profile. Admins can read all profiles.

CREATE POLICY "users_read_own_profile"
ON profiles FOR SELECT
USING (id = auth.uid());
CREATE POLICY "admins_read_all_profiles"
ON profiles FOR SELECT
USING ((auth.jwt() ->> 'role') = 'admin');

jobs

Organizations read their own studies. Testers read studies they are matched to.

CREATE POLICY "org_read_own_jobs"
ON jobs FOR SELECT
USING (
organization_id = (auth.jwt() -> 'organization_id')::uuid
);
CREATE POLICY "tester_read_matched_jobs"
ON jobs FOR SELECT
USING (
id IN (
SELECT job_id FROM sessions
WHERE tester_id = auth.uid()
AND status != 'cancelled'
)
);

session_events

Testers can insert events for their own active sessions. No direct reads from session_events by testers or businesses. Reads go through aggregate views.

CREATE POLICY "tester_insert_own_events"
ON session_events FOR INSERT
WITH CHECK (
session_id IN (
SELECT id FROM sessions
WHERE tester_id = auth.uid()
AND status = 'active'
)
);

session_notes

Testers can read and update notes for their own sessions.

CREATE POLICY "tester_read_own_notes"
ON session_notes FOR SELECT
USING (
session_id IN (
SELECT id FROM sessions
WHERE tester_id = auth.uid()
)
);
CREATE POLICY "tester_update_own_notes"
ON session_notes FOR UPDATE
USING (
session_id IN (
SELECT id FROM sessions
WHERE tester_id = auth.uid()
)
)
WITH CHECK (
session_id IN (
SELECT id FROM sessions
WHERE tester_id = auth.uid()
)
);