Skip to main content
Insito’s data model is relational and multi-tenant: every row ultimately belongs to one organization. The core tables below cover what your app and dashboard read and write; a handful of supporting tables (variables, usage counters, screen maps, integrations) are summarised at the end.

Entity diagram

Answers are not a separate table — they live as a JSONB array on each response row.

Core tables

organizations

The billing unit. One organization = one Stripe customer, one plan, one set of members.

members

Links a Supabase auth user to an organization with a role.

projects

One app = one project. Holds the project’s api_key (the proj_… secret your SDK uses).

surveys

A survey definition, linked to a project. Scheduling and behaviour toggles (show_branding, show_question_numbers, allow_back, autosave_progress, schedule window) are added by later migrations.

questions

Belongs to a survey. position is the order in the modal. welcome_screen and end_screen are steps in the flow, not answerable questions.

respondents

Created on first identify() per (project, external_user_id). Multiple devices share one respondent if you pass the same userId.

impressions

One row each time a survey modal is shown. Feeds the per-user show cap (maxShowsPerUser) and records shown_at.

responses

One row per response. Counts against the app’s monthly cap. Each element of answers is { questionId, type, value }, where value is a scalar or array (not a wrapper object):
  • nps: value: 9 (number 0–10)
  • rating: value: 4 (number)
  • multiple_choice: value: ["option_a", "option_b"] (array of strings)
  • open_text: value: "free text" (string)

Supporting tables

Deletes and history

  • Projects are soft-deleted (is_active = false).
  • Surveys can be hard-deleted from the dashboard, which removes their responses. Use status = "paused" to deactivate a survey without deleting it.
  • Respondents and responses are effectively immutable once written (partial responses are updated in place until completed).

RLS scope

Every table has Postgres Row Level Security. Dashboard access is scoped by Supabase auth + the members table (get_user_org_ids()), so members only see rows in their own organization. The SDK uses a project API key that binds every request to a single project. See Authentication for how API keys and JWTs authorize requests.