Documentation Index
Fetch the complete documentation index at: https://docs.insito.app/llms.txt
Use this file to discover all available pages before exploring further.
Insito’s data model is intentionally flat. Six tables cover everything
your app and dashboard read or write.
Entity diagram
Tables
organization
The billing unit. One organization = one Stripe customer, one
monthly response cap, one set of members.
| Column | Type | Notes |
|---|
id | uuid | PK |
name | text | Workspace name |
plan | enum | free, pro, scale, enterprise |
stripe_customer_id | text | Set after first paid plan |
member
Links a Supabase auth user to an organization with a role.
| Column | Type | Notes |
|---|
user_id | uuid | FK → auth.users.id |
organization_id | uuid | FK |
role | enum | owner, admin, editor, viewer |
project
One app = one project. Holds the project’s api_key (the proj_xxx
secret your SDK uses).
| Column | Type | Notes |
|---|
id | uuid | PK |
organization_id | uuid | FK |
name | text | Display name |
api_key | text | Project secret, format proj_xxx. Unique, indexed. |
platform | enum | react-native, flutter, web, other |
survey
A survey definition. Linked to a project.
| Column | Type | Notes |
|---|
id | uuid | PK |
project_id | uuid | FK |
name | text | Internal label |
trigger_key | text | ^[a-z][a-z0-9_]*$ |
status | enum | draft, active, inactive |
cooldown_days | int | Default 28 |
audience | jsonb | Platform + version filters |
question
Belongs to a survey. Position = order in the modal.
| Column | Type | Notes |
|---|
id | uuid | PK |
survey_id | uuid | FK |
type | enum | nps, rating, multiple_choice, open_text |
text | text | Shown to user |
options | jsonb | Type-dependent (choices, max length, etc.) |
is_required | bool | Block “Next” until answered |
position | int | 1-based |
respondent
Created on first identify() per (project, userId). Holds metadata
keyed by userId. Multiple devices share one respondent if you pass
the same userId.
| Column | Type | Notes |
|---|
id | uuid | PK |
project_id | uuid | FK |
user_id | text | Your stable identifier |
platform | text | From identify() |
app_version | text | From identify() |
metadata | jsonb | Free-form key/value |
response
One row per submitted survey. Counts against your plan.
| Column | Type | Notes |
|---|
id | uuid | PK |
survey_id | uuid | FK |
respondent_id | uuid | FK |
created_at | timestamptz | When the user tapped Submit |
platform | text | Snapshot at submit time |
app_version | text | Snapshot at submit time |
answer
One row per question per response.
| Column | Type | Notes |
|---|
id | uuid | PK |
response_id | uuid | FK |
question_id | uuid | FK |
value | jsonb | Type-dependent — see below |
value shape per question type:
nps: { "n": 0..10 }
rating: { "n": 1..5 }
multiple_choice: { "choices": ["a", "b"] } (always an array)
open_text: { "text": "..." }
Soft deletes and history
- Surveys are never hard-deleted. Toggling status to
inactive
keeps the data; the API just refuses to serve the survey to new
triggers.
- Questions removed from a survey are soft-deleted (
deleted_at
set). Historical answers still resolve.
- Respondents and responses are immutable once written. There’s no
edit-response surface in the SDK or dashboard.
RLS scope
Every row is scoped to one organization_id via Postgres RLS. The
SDK uses a project API key that’s tied to one organization; the
dashboard uses Supabase auth + the member table.
Cross-organization reads/writes return 404 — never 403, on purpose,
so tenant existence isn’t leaked.
See Authentication for how API keys and JWTs
authorize requests.