Database Schema
Terra uses PostgreSQL via Supabase with 35+ tables organized into logical domains.This document provides a tiered reference: detailed coverage for core tables, brief listings for supporting tables.
Schema Overview
Core Tables (Detailed)
forms
The central table storing form definitions and settings.draft_schema/published_schema— Recursive JSON form definitionssettings— JSONB blob for branding, deadlines, etc.folder_id— Which workspace contains this formsubmission_prefix— Custom prefix for reference IDs (e.g., “RENT-”)
form_submissions
Individual submissions with answers stored as JSONB.answers—{"full-name": "Jane Doe", "income": 45000, ...}reference_id— Human-readable ID (e.g., “RENT-2024-001”)status—submitted,under_review,approved,denied, etc.plaid_access_tokens— Encrypted Plaid tokens per field
user_profiles
Admin users with roles (synced from WorkOS).super_admin— Full system accessadmin— Dashboard access, manage formsuser— Limited team memberapplicant— Portal only, view own applications
applicants
Applicant identity records (separate from admin users).workos_user_id) without being an admin.
folders (Workspaces)
Logical grouping for forms with optional custom domains.async_operations
Persistent queue for background jobs.airtable_connections
OAuth tokens and field mappings for Airtable sync.webhook_configs
Webhook endpoints for forms.notification_events
Log of all sent notifications.Supporting Tables (Brief)
Agencies & Multi-tenancy
| Table | Purpose |
|---|---|
agencies | External partner organizations |
agency_members | Users belonging to agencies |
agency_clients | Clients managed by agencies |
agency_form_access | Form-level access grants |
folder_members | User access to workspaces |
folder_custom_domains | Custom domains per folder |
Form Features
| Table | Purpose |
|---|---|
form_versions | Historical schema versions |
form_templates | Reusable form templates |
form_sessions | Active form view sessions |
form_visits | Page view analytics |
form_insights_daily | Aggregated daily metrics |
form_import_jobs | AI form import tracking |
Notifications
| Table | Purpose |
|---|---|
notification_providers | Provider credentials (Resend, Twilio) |
notification_templates | Custom email/SMS templates |
form_notification_settings | Per-form notification config |
Applicant Data
| Table | Purpose |
|---|---|
applicant_profiles | Extended applicant info |
applicant_pii | Encrypted sensitive data |
applicant_bank_accounts | Bank account details |
Webhooks & Events
| Table | Purpose |
|---|---|
webhook_events | Outbound webhook delivery log |
webhook_inbound_events | Inbound webhook processing |
Audit & History
| Table | Purpose |
|---|---|
audit_logs | All user actions |
submission_status_history | Status change audit trail |
airtable_sync_events | Airtable sync history |
System
| Table | Purpose |
|---|---|
organization_settings | Global branding, localization |
system_settings | Platform-wide configuration |
Row Level Security
Most tables use service role access (RLS bypassed) with application-level permission checks. RLS is enabled but permissive for background operations:Indexes
Key indexes for query performance:Migrations
Terra uses numbered SQL migrations inapps/terra/migrations/: