MDX Limo
Clay Data Model Proposal

Clay Data Model Proposal

Purpose

This document proposes a normalized database model for the fractional executives directory and jobs board.

The goal is to make it easy to answer two questions before implementation:

  1. What is the canonical app data model we want to support?
  2. Which of those fields can realistically come from Clay, and which must be derived or managed in-app?

This proposal assumes PostgreSQL or Supabase Postgres.

Design Principles

  • Keep Clay as a source system, not the canonical application schema.
  • Normalize shared taxonomies such as roles, industries, locations, and specialties.
  • Keep rich profile content in child tables instead of large denormalized blobs.
  • Store raw Clay payloads so we can remap or reprocess records without losing source context.
  • Derive search facets, market stats, related profiles, and landing-page aggregates from canonical tables.
  • Keep a clear separation between source ingestion state and publish state.

What Should Be Canonical vs Derived

Canonical

  • Roles
  • Industries
  • Locations
  • Specialties
  • Executive profiles
  • Executive experience
  • Executive education
  • Executive certifications
  • Executive reviews
  • Executive case studies
  • Executive verification state
  • Jobs
  • Job-to-industry relationships
  • Profile claim requests and ownership state
  • Clay ingestion metadata

Derived

  • Search results
  • Search facets
  • Market stats
  • Role, location, and industry landing-page payloads
  • Similar executives
  • Review count and average rating cache
  • Human-readable compensation summary
  • Human-readable location labels for some jobs

Suggested Enums

Executive Availability

  • AVAILABLE
  • LIMITED
  • UNAVAILABLE
  • BOOKED

Claim Status

  • UNCLAIMED
  • CLAIMED
  • VERIFIED

Travel Willingness

  • LOCAL
  • REGIONAL
  • NATIONAL
  • INTERNATIONAL

Verification Check Type

  • IDENTITY
  • CREDENTIALS
  • TRACK_RECORD

Verification Status

  • VERIFIED
  • PENDING
  • UNVERIFIED

Job Employment Type

  • CONTRACTOR
  • PART_TIME
  • FULL_TIME
  • TEMPORARY

Job Remote Type

  • REMOTE
  • HYBRID
  • ONSITE

Publish Status

  • DRAFT
  • REVIEW
  • PUBLISHED
  • ARCHIVED

Ingestion Status

  • PENDING
  • SUCCEEDED
  • FAILED
  • SKIPPED

Core Taxonomy Tables

roles

Canonical leadership role taxonomy used across directory and jobs.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniqueExample: fractional-cfo
titletextExample: CFO
full_titletextExample: Chief Financial Officer
descriptiontextSEO + directory copy
is_activebooleanSoft disable
created_attimestamptzAudit
updated_attimestamptzAudit

industries

Canonical industry taxonomy.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniqueExample: saas
nametextExample: SaaS
descriptiontextOptional taxonomy description
is_activebooleanSoft disable
created_attimestamptzAudit
updated_attimestamptzAudit

locations

Canonical geographic taxonomy. Executives should reference this directly. Jobs can reference it when clean, while still keeping a display string.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniqueExample: austin-tx
citytextExample: Austin
state_regiontext nullableExample: TX
country_codetextExample: US
country_nametextExample: USA
metrotext nullableExample: Greater Austin
display_nametextExample: Austin, TX
latnumeric nullableOptional
lngnumeric nullableOptional
is_activebooleanSoft disable
created_attimestamptzAudit
updated_attimestamptzAudit

specialties

Canonical specialty tags used for search and richer profile context.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniqueExample: forecasting
nametextExample: Forecasting
is_activebooleanSoft disable
created_attimestamptzAudit
updated_attimestamptzAudit

Executive Directory Tables

executives

This is the canonical profile record. It should contain the core fields required for both cards and full profile pages.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniquePublic URL slug
first_nametextRequired
last_nametextRequired
full_nametextCan be derived but storing is pragmatic
headlinetextCard + profile hero
biotext nullableFull profile
photo_urltext nullableOptional
role_iduuid fk -> roles.idRequired
location_iduuid fk -> locations.idRequired
years_experienceinteger nullableSearch + profile hero
availabilityenumAVAILABLE, etc.
remote_availablebooleanProfile detail
travel_willingnessenum nullableProfile detail
minimum_engagementtext nullableExample: 3 months
linkedin_urltext nullableOptional
website_urltext nullableOptional
hourly_mininteger nullableNumeric sort/filter field
hourly_maxinteger nullableNumeric sort/filter field
monthly_mininteger nullableNumeric detail field
monthly_maxinteger nullableNumeric detail field
claim_statusenumUI trust state
featuredbooleanDirectory ranking flag
highlight_outcometext nullableCard summary line
review_count_cachedinteger default 0Denormalized cache
avg_rating_cachednumeric nullableDenormalized cache
last_confirmed_attimestamptz nullableTrust surface
published_attimestamptz nullablePublish state
statusenumDRAFT, REVIEW, PUBLISHED, ARCHIVED
source_systemtextExample: clay
source_record_idtext nullableStable Clay row id
source_synced_attimestamptz nullableLast sync timestamp
raw_payloadjsonb nullableRaw source payload
created_attimestamptzAudit
updated_attimestamptzAudit

executive_industries

Many-to-many join between executives and industries.

ColumnTypeNotes
executive_iduuid fk -> executives.idRequired
industry_iduuid fk -> industries.idRequired
sort_orderinteger default 0Preserve preferred ordering
created_attimestamptzAudit

Unique constraint:

  • (executive_id, industry_id)

executive_specialties

Many-to-many join between executives and specialties.

ColumnTypeNotes
executive_iduuid fk -> executives.idRequired
specialty_iduuid fk -> specialties.idRequired
sort_orderinteger default 0Preserve preferred ordering
created_attimestamptzAudit

Unique constraint:

  • (executive_id, specialty_id)

executive_experience

Structured career timeline entries.

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
company_nametextDenormalized for now
role_titletextExample: Interim CFO
descriptiontext nullableTimeline copy
start_datedate nullablePartial dates okay
end_datedate nullableNull if current
is_currentbooleanUI convenience
sort_orderinteger default 0Preserve timeline order
source_record_idtext nullableOptional source mapping
created_attimestamptzAudit
updated_attimestamptzAudit

executive_education

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
institutiontextRequired
degreetext nullableOptional
fieldtext nullableOptional
year_endinteger nullableOptional
sort_orderinteger default 0Preserve display order
created_attimestamptzAudit
updated_attimestamptzAudit

executive_certifications

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
nametextShort name, example: PMP
full_nametext nullableFull credential name
year_obtainedinteger nullableOptional
sort_orderinteger default 0Preserve display order
created_attimestamptzAudit
updated_attimestamptzAudit

executive_reviews

Optional but valuable. These power trust signals and rating metadata.

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
author_nametextRequired
author_titletext nullableOptional
author_companytext nullableOptional
ratingnumeric1-5 range
contenttextRequired
verifiedbooleanSource trust
published_attimestamptz nullableOptional moderation
source_record_idtext nullableOptional source mapping
created_attimestamptzAudit
updated_attimestamptzAudit

executive_case_studies

Structured outcome stories.

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
titletextRequired
situationtext nullableOptional
actiontext nullableOptional
outcometext nullableOptional
metrictext nullableExample: Forecast accuracy improved in 6 weeks
sort_orderinteger default 0Preserve display order
source_record_idtext nullableOptional source mapping
created_attimestamptzAudit
updated_attimestamptzAudit

executive_verifications

This stores the trust badges shown on profile pages. It is more flexible than hard-coding three badge fields on the executives table.

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
check_typeenumIDENTITY, CREDENTIALS, TRACK_RECORD
statusenumVERIFIED, PENDING, UNVERIFIED
detailtext nullableUI detail text
checked_attimestamptz nullableOptional
sourcetext nullableExample: manual-review, clay, supabase-claim
created_attimestamptzAudit
updated_attimestamptzAudit

Unique constraint:

  • (executive_id, check_type)

Jobs Board Tables

jobs

Canonical job posting record. The current app treats location and compensation as free text in some places, but the database should store structured fields and derive summary strings from them.

ColumnTypeNotes
iduuid pkInternal primary key
slugtext uniquePublic URL slug
titletextRequired
company_nametextRequired
location_summarytextDisplay string, example: Remote (US) with monthly Boston travel
primary_location_iduuid fk -> locations.id nullableUse when cleanly mappable
role_iduuid fk -> roles.idRequired
summarytextCard + hero copy
descriptiontextFull detail page + JobPosting schema
apply_urltextRequired
posted_attimestamptzRequired
employment_typeenumRequired
remote_typeenum nullablePrefer explicit value over inference
compensation_summarytext nullableHuman-readable display
comp_min_amountinteger nullableStructured sort/filter field
comp_max_amountinteger nullableStructured sort/filter field
comp_periodtext nullableExample: hour, month, year
comp_currencytext nullableExample: USD
curated_attimestamptz nullableLast reviewed or editorial approval date
community_topic_hinttext nullableOptional community routing
published_attimestamptz nullablePublish state
statusenumDRAFT, REVIEW, PUBLISHED, ARCHIVED
source_systemtextExample: clay
source_record_idtext nullableStable Clay row id
source_synced_attimestamptz nullableLast sync timestamp
raw_payloadjsonb nullableRaw source payload
created_attimestamptzAudit
updated_attimestamptzAudit

job_industries

Many-to-many join between jobs and industries.

ColumnTypeNotes
job_iduuid fk -> jobs.idRequired
industry_iduuid fk -> industries.idRequired
sort_orderinteger default 0Preserve preferred ordering
created_attimestamptzAudit

Unique constraint:

  • (job_id, industry_id)

job_responsibilities

Separate table if we want fully structured editability and source tracking.

ColumnTypeNotes
iduuid pkInternal primary key
job_iduuid fk -> jobs.idRequired
bodytextRequired
sort_orderinteger default 0Preserve display order
created_attimestamptzAudit
updated_attimestamptzAudit

job_requirements

Separate table if we want fully structured editability and source tracking.

ColumnTypeNotes
iduuid pkInternal primary key
job_iduuid fk -> jobs.idRequired
bodytextRequired
sort_orderinteger default 0Preserve display order
created_attimestamptzAudit
updated_attimestamptzAudit

Ownership and Trust Workflow Tables

These align with the existing claim flow already present in the app.

profile_claim_requests

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
user_iduuid nullableSupabase auth user id if available
emailtext nullableUseful for review
statustextExample: RECEIVED, IN_REVIEW, APPROVED, REJECTED
submitted_payloadjsonbRequest body
review_notestext nullableInternal ops notes
created_attimestamptzAudit
reviewed_attimestamptz nullableAudit

profile_ownerships

ColumnTypeNotes
iduuid pkInternal primary key
executive_iduuid fk -> executives.idRequired
user_iduuidSupabase auth user id
statustextExample: ACTIVE, REVOKED, PENDING
verified_attimestamptz nullableClaim verification timestamp
created_attimestamptzAudit
ended_attimestamptz nullableAudit

Clay Ingestion Tables

These keep source synchronization clean and auditable.

ingestion_runs

ColumnTypeNotes
iduuid pkInternal primary key
sourcetextExample: clay
entity_typetextExample: executive, job
statusenumPENDING, SUCCEEDED, FAILED, SKIPPED
started_attimestamptzAudit
finished_attimestamptz nullableAudit
metricsjsonb nullableCounts, warnings, timings
created_attimestamptzAudit

ingestion_records

ColumnTypeNotes
iduuid pkInternal primary key
run_iduuid fk -> ingestion_runs.idRequired
entity_typetextexecutive, job, etc.
source_record_idtext nullableClay row id
target_iduuid nullableCanonical row id
actiontextINSERT, UPDATE, SKIP, DELETE, ERROR
statusenumPENDING, SUCCEEDED, FAILED, SKIPPED
error_messagetext nullableFailure details
raw_payloadjsonb nullableRaw input row
normalized_payloadjsonb nullablePost-transform row
created_attimestamptzAudit
updated_attimestamptzAudit

Minimum Clay Support by Entity

This section is the reverse-engineering checklist. It shows what Clay must provide for a useful first import.

Executive Minimum Viable Fields

  • External unique record id
  • First name
  • Last name
  • Public name or ability to derive one
  • Headline
  • Role
  • Primary location
  • At least one industry
  • Years of experience or ability to approximate
  • Availability
  • Optional hourly or monthly rates
  • Bio
  • Optional LinkedIn URL
  • Optional website URL

Executive Nice-to-Have Fields

  • Photo URL
  • Specialties
  • Experience timeline
  • Education
  • Certifications
  • Reviews
  • Case studies
  • Last confirmed date
  • Trust or verification inputs
  • Featured ranking signal

Executive Fields We Can Derive In-App

  • slug
  • full_name
  • claim_status defaulting to UNCLAIMED
  • review_count_cached
  • avg_rating_cached
  • highlight_outcome fallback
  • similarExecutives

Job Minimum Viable Fields

  • External unique record id
  • Title
  • Company name
  • Role
  • At least one industry
  • Location display string
  • Summary
  • Description
  • Apply URL
  • Posted date
  • Employment type

Job Nice-to-Have Fields

  • Structured location
  • Explicit remote type
  • Responsibilities
  • Requirements
  • Structured compensation min and max
  • Compensation period
  • Curated or reviewed timestamp
  • Community topic hint

Job Fields We Can Derive In-App

  • slug
  • compensation_summary from structured compensation
  • remote_type from location text if missing
  • Related jobs

Fields That Probably Do Not Belong in Clay

These are better owned by the app or ops layer.

  • Publish status
  • Editorial review status
  • Featured flags unless editorially managed in Clay
  • Claim requests
  • Profile ownership records
  • Verification badge decisions
  • Ingestion run metadata
  • Search stats and rollups

Proposed Canonical JSON Shape for an Executive

1{ 2 "slug": "sarah-chen-fractional-cfo", 3 "firstName": "Sarah", 4 "lastName": "Chen", 5 "fullName": "Sarah Chen", 6 "headline": "Fractional CFO for Series A-C SaaS and Fintech teams", 7 "bio": "Sarah helps founders move from reporting to real financial control.", 8 "roleSlug": "fractional-cfo", 9 "locationSlug": "austin-tx", 10 "industrySlugs": ["saas", "fintech"], 11 "specialtySlugs": ["forecasting", "fundraising"], 12 "yearsExperience": 18, 13 "availability": "AVAILABLE", 14 "remoteAvailable": true, 15 "travelWillingness": "NATIONAL", 16 "minimumEngagement": "3 months", 17 "linkedinUrl": "https://www.linkedin.com/in/sarah-chen-fractional-cfo", 18 "websiteUrl": "https://sarahchen.example.com", 19 "rates": { 20 "hourlyMin": 275, 21 "hourlyMax": 420, 22 "monthlyMin": 9000, 23 "monthlyMax": 18000 24 }, 25 "claimStatus": "UNCLAIMED", 26 "featured": false, 27 "highlightOutcome": "Improved forecasting confidence before board meetings.", 28 "experience": [], 29 "education": [], 30 "certifications": [], 31 "reviews": [], 32 "caseStudies": [], 33 "verificationBadges": [] 34}

Proposed Canonical JSON Shape for a Job

1{ 2 "slug": "series-b-saas-fractional-cfo-remote-us", 3 "title": "Fractional CFO (Series B SaaS)", 4 "companyName": "Northbound Analytics", 5 "locationSummary": "Remote (US)", 6 "primaryLocationSlug": null, 7 "roleSlug": "fractional-cfo", 8 "industrySlugs": ["saas", "ai"], 9 "summary": "Own forecasting, board reporting, and fundraising readiness.", 10 "description": "Northbound Analytics is hiring a part-time fractional CFO...", 11 "responsibilities": [ 12 "Lead board and investor financial narratives." 13 ], 14 "requirements": [ 15 "10+ years in senior finance leadership." 16 ], 17 "applyUrl": "https://example.com/jobs/series-b-saas-fractional-cfo", 18 "postedAt": "2026-02-20T00:00:00.000Z", 19 "employmentType": "PART_TIME", 20 "remoteType": "REMOTE", 21 "compensationSummary": "$12,000-$18,000/month", 22 "compMinAmount": 12000, 23 "compMaxAmount": 18000, 24 "compPeriod": "month", 25 "compCurrency": "USD", 26 "curatedAt": null, 27 "communityTopicHint": null 28}
  • Implement taxonomy tables first
  • Implement executives, executive_industries, executive_specialties
  • Implement jobs and job_industries
  • Add experience, reviews, case_studies, and verifications after the first Clay mapping pass
  • Keep raw_payload on both executives and jobs from day one
  • Keep ingestion_runs and ingestion_records from day one

Questions to Answer Before Building

  • Will Clay provide normalized roles and industries, or do we need mapping tables?
  • Will Clay provide one row per executive or one row per executive-role-market combination?
  • Will jobs be curated manually after import, or should Clay control publish state?
  • Do we want responsibilities and requirements as arrays in Clay, or as multi-row related records?
  • Do we want to trust Clay compensation values enough to support sorting and filtering?
  • Do we want to store executive trust and verification fields in Clay, or keep that purely operational in the app?

Bottom Line

Clay should be treated as the source of many content fields, but not the final app schema.

The canonical model should stay normalized and app-oriented:

  • taxonomy tables for shared lookups
  • one core executives table plus child tables for rich profile data
  • one core jobs table plus child tables for structured job bullets
  • separate trust, ownership, and ingestion tables
  • derived search and market pages built from query logic, not stored as primary records