For Engineering
Database schema
Generated from
laurelin/db.jsinitSchema()byscripts/generate-schema-docs.js. Do not edit by hand — your changes will be overwritten on the next deploy.
All primary keys are TEXT UUIDs via crypto.randomUUID() unless noted. JSON fields (email_domains, tags, source_metadata) are stored as TEXT. Soft-delete via deleted_at. Every UPDATE on companies/people/projects must call logChanges().
Total tables: 35
Core entities
people
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
name |
TEXT | NOT NULL |
email |
TEXT | |
role |
TEXT | |
phone |
TEXT | |
linkedin_url |
TEXT | |
telegram_handle |
TEXT | |
slack_handle |
TEXT | |
person_type |
TEXT | NOT NULL; default 'external'; one of: internal, external |
contact_type |
TEXT | default 'working' |
primary_flag |
INTEGER | default 0 |
notes |
TEXT | default '' |
signature_parsed_at |
TEXT | |
signature_parse_attempts |
INTEGER | default 0 |
created_at |
TEXT | default (datetime('now')) |
companies
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
name |
TEXT | NOT NULL |
email_domains |
TEXT | default '[]' |
company_types |
TEXT | default '[]' |
importance |
TEXT | default 'medium'; one of: high, medium, low |
stage |
TEXT | default 'prospect'; one of: dormant, prospect, exploring, active, core, passed, in_market |
ball_with |
TEXT | default 'them'; one of: us, them |
target_deal_type |
TEXT | default '' |
icp |
TEXT | default '' |
health |
TEXT | default '' |
owner_id |
TEXT | |
notes |
TEXT | default '' |
tags |
TEXT | default '[]' |
review_link |
TEXT | |
created_at |
TEXT | default (datetime('now')) |
updated_at |
TEXT | default (datetime('now')) |
deleted_at |
TEXT |
projects
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
name |
TEXT | NOT NULL |
type |
TEXT | default 'Other'; one of: Deal, Fund, Product, Infrastructure, Biz Dev, Campaign, Research, Legal, Automation, Finance, Diligence, Internal, Other |
parent_id |
TEXT | |
next_step |
TEXT | |
blocker |
TEXT | |
notes |
TEXT | default '' |
strategy |
TEXT | default '' |
phase |
TEXT | default '' |
summary |
TEXT | default '' |
importance |
INTEGER | default 5 |
deal_type |
TEXT | default '' |
coinvestors |
TEXT | default '' |
deal_stage |
TEXT | |
project_stage |
TEXT | |
intensity |
INTEGER | default 0 |
deal_size_estimate |
REAL | |
status |
TEXT | default 'active'; one of: sketch, hot, active, background, blocked, on-hold, completed, cancelled, deleted |
health |
TEXT | default '' |
created_at |
TEXT | default (datetime('now')) |
updated_at |
TEXT | default (datetime('now')) |
interactions
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
date |
TEXT | NOT NULL |
company_id |
TEXT | |
type |
TEXT | one of: email, call, meeting, note, telegram |
source |
TEXT | default 'manual'; one of: outlook, notion, telegram, manual, slack, calendar |
direction |
TEXT | one of: inbound, outbound, mutual, internal |
subject |
TEXT | |
summary |
TEXT | |
logged_by_id |
TEXT | |
source_id |
TEXT | |
source_thread_id |
TEXT | |
source_metadata |
TEXT | |
project_id |
TEXT | |
created_at |
TEXT | default (datetime('now')) |
Joins
affiliations
| Column | Type | Notes |
|---|---|---|
person_id |
TEXT | NOT NULL |
company_id |
TEXT | NOT NULL |
role_at_company |
TEXT | |
started_at |
TEXT | default (date('now')) |
ended_at |
TEXT |
interaction_companies
| Column | Type | Notes |
|---|---|---|
interaction_id |
TEXT | NOT NULL |
company_id |
TEXT | NOT NULL |
interaction_people
| Column | Type | Notes |
|---|---|---|
interaction_id |
TEXT | NOT NULL |
person_id |
TEXT | NOT NULL |
role |
TEXT | default 'participant'; one of: participant, logged_by |
project_companies
| Column | Type | Notes |
|---|---|---|
project_id |
TEXT | NOT NULL |
company_id |
TEXT | NOT NULL |
project_people
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
project_id |
TEXT | NOT NULL |
person_id |
TEXT | NOT NULL |
role |
TEXT | default 'member'; one of: manager, member, watcher, contact |
added_by |
TEXT | default 'manual'; one of: manual, auto |
source_interaction_id |
TEXT | |
created_at |
TEXT | default (datetime('now')) |
company_parents
| Column | Type | Notes |
|---|---|---|
child_id |
TEXT | NOT NULL |
parent_id |
TEXT | NOT NULL |
relation_type |
TEXT | default 'parent_child' |
note |
TEXT |
Project tracking
project_milestones
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
project_id |
TEXT | NOT NULL |
label |
TEXT | NOT NULL |
date |
TEXT | |
notes |
TEXT | default '' |
created_at |
TEXT | default (datetime('now')) |
project_status_updates
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
project_id |
TEXT | NOT NULL |
week_of |
TEXT | NOT NULL |
body |
TEXT | default '' |
health |
TEXT | default '' |
no_change |
INTEGER | default 0 |
submitted_by |
TEXT | |
submitted_at |
TEXT | default (datetime('now')) |
project_links
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
project_id |
TEXT | NOT NULL |
label |
TEXT | NOT NULL |
url |
TEXT | NOT NULL |
type |
TEXT | default 'other'; one of: data-room, memo, legal, model, presentation, other |
source |
TEXT | default 'other'; one of: notion, sharepoint, google-drive, other |
document_type |
TEXT | default '' |
link_status |
TEXT | default 'working'; one of: working, final |
created_at |
TEXT | default (datetime('now')) |
company_links
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
company_id |
TEXT | NOT NULL |
label |
TEXT | NOT NULL |
url |
TEXT | NOT NULL |
type |
TEXT | default 'other'; one of: website, github, review, linkedin, crunchbase, data-room, other |
created_at |
TEXT | default (datetime('now')) |
key_dates
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
entity_type |
TEXT | NOT NULL; one of: company, project |
entity_id |
TEXT | NOT NULL |
label |
TEXT | NOT NULL |
date |
TEXT | |
importance |
TEXT | default 'medium'; one of: high, medium, low |
flexible |
INTEGER | default 1 |
notes |
TEXT | default '' |
created_at |
TEXT | default (datetime('now')) |
Sync pipeline
sync_inbox
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
source |
TEXT | NOT NULL; one of: outlook, calendar, slack, notion, telegram |
source_id |
TEXT | NOT NULL |
source_thread_id |
TEXT | |
submitted_by |
TEXT | |
submitted_at |
TEXT | default (datetime('now')) |
entity_type |
TEXT | NOT NULL; one of: interaction, project, company, person |
entity_data |
TEXT | NOT NULL; default '{}' |
suggested_company_id |
TEXT | |
suggested_company_name |
TEXT | |
suggested_person_ids |
TEXT | default '[]' |
suggested_project_id |
TEXT | |
relevant_to |
TEXT | default '[]' |
status |
TEXT | default 'pending'; one of: pending, approved, dismissed, merged |
reviewed_by |
TEXT | |
reviewed_at |
TEXT | |
review_notes |
TEXT | |
target_domain |
TEXT | NOT NULL; default '' |
sync_watermarks
| Column | Type | Notes |
|---|---|---|
user_id |
TEXT | NOT NULL |
source |
TEXT | NOT NULL |
channel |
TEXT | default '' |
last_sync_at |
TEXT | NOT NULL |
last_source_id |
TEXT |
sync_skip_rules
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
rule_type |
TEXT | NOT NULL; one of: sender, domain, subject_pattern, source_id_prefix, contact_skip, slack_channel, telegram_handle |
pattern |
TEXT | NOT NULL |
source |
TEXT | default 'all'; one of: outlook, calendar, slack, telegram, all |
created_by |
TEXT | |
created_at |
TEXT | default (datetime('now')) |
reason |
TEXT | default '' |
oauth_tokens
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
user_id |
TEXT | NOT NULL |
service |
TEXT | NOT NULL; one of: outlook, slack, google |
access_token |
TEXT | NOT NULL |
refresh_token |
TEXT | |
expires_at |
TEXT | |
scope |
TEXT | |
created_at |
TEXT | default (datetime('now')) |
updated_at |
TEXT | default (datetime('now')) |
email_do_not_track
| Column | Type | Notes |
|---|---|---|
email |
TEXT | PK |
added_by |
TEXT | |
reason |
TEXT | default '' |
added_at |
TEXT | default (datetime('now')) |
telegram_connections
| Column | Type | Notes |
|---|---|---|
business_connection_id |
TEXT | PK |
internal_person_id |
TEXT | NOT NULL |
user_chat_id |
INTEGER | |
is_enabled |
INTEGER | default 1 |
can_reply |
INTEGER | default 0 |
connected_at |
TEXT | default (datetime('now')) |
revoked_at |
TEXT |
telegram_chat_scope
| Column | Type | Notes |
|---|---|---|
chat_id |
INTEGER | NOT NULL |
internal_person_id |
TEXT | NOT NULL |
chat_title |
TEXT | |
chat_type |
TEXT | |
enabled |
INTEGER | default 1 |
company_id |
TEXT | |
tagged_by |
TEXT | |
tagged_at |
TEXT | |
first_seen_at |
TEXT | default (datetime('now')) |
last_message_at |
TEXT | |
last_changed_by |
TEXT | |
last_changed_at |
TEXT |
telegram_shared_chat_overrides
| Column | Type | Notes |
|---|---|---|
chat_id |
INTEGER | PK |
team_scope |
TEXT | NOT NULL; one of: enabled, disabled |
set_by |
TEXT | |
set_at |
TEXT | default (datetime('now')) |
slack_channel_map
| Column | Type | Notes |
|---|---|---|
channel_id |
TEXT | NOT NULL |
team_id |
TEXT | NOT NULL |
company_id |
TEXT | |
channel_name |
TEXT | |
channel_type |
TEXT | |
is_external |
INTEGER | default 0 |
first_seen_at |
TEXT | default (datetime('now')) |
last_message_at |
TEXT | |
last_changed_by |
TEXT | |
last_changed_at |
TEXT |
Lost-thread detection
lost_thread_candidates
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
conversation_id |
TEXT | NOT NULL |
detected_for_user_id |
TEXT | NOT NULL |
subject |
TEXT | |
last_message_id |
TEXT | |
last_message_date |
TEXT | NOT NULL |
last_message_direction |
TEXT | one of: inbound, outbound |
last_sender_email |
TEXT | |
last_sender_name |
TEXT | |
snippet |
TEXT | |
days_stale |
INTEGER | |
urgency_tier |
TEXT | default 'normal'; one of: normal, emergency |
urgency_reason |
TEXT | |
detected_at |
TEXT | default (datetime('now')) |
resolved_at |
TEXT | |
resolved_interaction_id |
TEXT | |
status |
TEXT | NOT NULL; default 'pending'; one of: pending, dismissed, resolved |
dismissed_scope |
TEXT | one of: once, forever |
dismissed_until_message_id |
TEXT | |
draft_requested_at |
TEXT | |
draft_intent |
TEXT | |
draft_voice |
TEXT | |
draft_started_at |
TEXT | |
draft_completed_at |
TEXT | |
draft_body |
TEXT | |
draft_body_preview |
TEXT | |
content_hash |
TEXT |
lost_thread_candidate_people
| Column | Type | Notes |
|---|---|---|
candidate_id |
TEXT | NOT NULL |
email |
TEXT | NOT NULL |
person_id |
TEXT |
lost_thread_candidate_companies
| Column | Type | Notes |
|---|---|---|
candidate_id |
TEXT | NOT NULL |
company_id |
TEXT | NOT NULL |
System
changelog
| Column | Type | Notes |
|---|---|---|
id |
INTEGER | PK |
record_type |
TEXT | NOT NULL |
record_id |
TEXT | NOT NULL |
field |
TEXT | |
old_value |
TEXT | |
new_value |
TEXT | |
changed_by |
TEXT | |
changed_at |
TEXT | default (datetime('now')) |
settings
| Column | Type | Notes |
|---|---|---|
key |
TEXT | PK |
value |
TEXT |
valinor_focus
| Column | Type | Notes |
|---|---|---|
kind |
TEXT | NOT NULL; one of: week, month, annual |
period_key |
TEXT | NOT NULL |
body |
TEXT | NOT NULL; default '' |
updated_at |
TEXT | default (datetime('now')) |
updated_by |
TEXT |
Other
pipeline_checkpoints
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
label |
TEXT | default '' |
trigger |
TEXT | default 'manual'; one of: manual, pre-pull, pre-push, pre-restore |
created_by |
TEXT | default '' |
created_at |
TEXT | default (datetime('now')) |
snapshot |
TEXT | NOT NULL |
slack_channel_members
| Column | Type | Notes |
|---|---|---|
channel_id |
TEXT | NOT NULL |
team_id |
TEXT | NOT NULL |
slack_user_id |
TEXT | NOT NULL |
joined_at |
TEXT | default (datetime('now')) |
personal_blocks
| Column | Type | Notes |
|---|---|---|
id |
TEXT | PK |
user_id |
TEXT | NOT NULL |
rule_type |
TEXT | NOT NULL; one of: email, domain, telegram_handle, slack_channel, slack_user, subject_pattern |
pattern |
TEXT | NOT NULL |
source |
TEXT | NOT NULL; default 'all'; one of: outlook, calendar, slack, telegram, notion, all |
reason |
TEXT | default '' |
created_at |
TEXT | default (datetime('now')) |
project_relations
| Column | Type | Notes |
|---|---|---|
project_id |
TEXT | NOT NULL |
related_project_id |
TEXT | NOT NULL |