Database Reference
This page lists Pulse's domain tables. All tables live in your own Supabase project — not ARK's. RLS is enabled on every table.
Shared platform tables
profiles— user metadata and timezone.workspaces— tenant boundary.workspace_members— user ↔ workspace with role.
Pulse domain tables
teams
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
workspace_id | uuid FK → workspaces | |
name | text | |
digest_location | jsonb | {channel: "slack|comms|email", target: "..."}. |
timezone_default | text | Fallback when users don't have one set. |
created_at | timestamptz |
team_members
| Column | Type | Notes |
|---|---|---|
team_id | uuid FK → teams | |
user_id | uuid FK → auth.users | |
role | text | lead, member. |
checkins
Recurring standups.
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
team_id | uuid FK → teams | |
name | text | |
schedule | jsonb | rrule/CRON + working-hours config. |
is_enabled | bool | |
response_window_hours | int | Default 4. |
digest_schedule | jsonb | When to post the digest. |
delivery_channel_default | text | |
created_by | uuid FK → auth.users |
checkin_questions
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
checkin_id | uuid FK → checkins | |
position | int | |
type | text | See Standups. |
prompt | text | |
config | jsonb | Type-specific settings. |
conditions | jsonb | Branching logic. |
surveys
One-off or recurring surveys.
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
workspace_id | uuid FK → workspaces | |
title | text | |
kind | text | survey, poll, enps, retro, postmortem. |
is_anonymous | bool | |
schedule | jsonb | Null for one-off; rrule for recurring. |
opens_at, closes_at | timestamptz | |
created_by | uuid FK → auth.users |
survey_questions
Same shape as checkin_questions but tied to survey_id.
responses
Answers to checkin or survey questions.
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
workspace_id | uuid FK → workspaces | |
source_kind | text | checkin, survey, poll. |
source_id | uuid | checkin_id or survey_id. |
question_id | uuid | checkin_questions.id or survey_questions.id. |
user_id | uuid FK → auth.users | Null for anonymous. |
answer | jsonb | Type-specific payload. |
submitted_at | timestamptz | |
delivery_channel | text | How they answered. |
digests
Compiled digest instances.
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
checkin_id | uuid FK → checkins | Null for survey digests. |
survey_id | uuid FK → surveys | Null for checkin digests. |
date | date | The day covered. |
content | text | Rendered markdown. |
delivered_at | timestamptz | |
delivery_target | jsonb | Channel + target. |
reminders
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
workspace_id | uuid FK → workspaces | |
creator_id | uuid FK → auth.users | |
target_kind | text | self, user, team. |
target_id | uuid | |
message | text | |
schedule | jsonb | rrule/CRON + working-hours. |
delivery_channel | text | |
next_fire_at | timestamptz | Updated after each fire. |
is_enabled | bool |
reminder_events
Audit log of reminder fires, snoozes, and acks.
slack_installations
One row per connected Slack workspace.
| Column | Type | Notes |
|---|---|---|
id | uuid PK | |
workspace_id | uuid FK → workspaces | |
slack_team_id | text | |
access_token | text | Encrypted at rest via Supabase Vault. |
bot_user_id | text | |
scopes | text[] | |
installed_at | timestamptz |
slack_user_mappings
| Column | Type | Notes |
|---|---|---|
slack_installation_id | uuid FK → slack_installations | |
slack_user_id | text | |
user_id | uuid FK → auth.users | Override mapping. |
scheduler_runs
Audit log for pulse-scheduler fires.
Relationships at a glance
workspaces
├─ teams
│ ├─ team_members
│ └─ checkins
│ ├─ checkin_questions
│ └─ digests
├─ surveys
│ └─ survey_questions
├─ responses (polymorphic on source_kind)
├─ reminders
│ └─ reminder_events
└─ slack_installations
└─ slack_user_mappings
RLS summary
teams,checkins,surveys: workspace-scoped.responses: workspace-scoped. Foris_anonymous=truerows, even admins can only read aggregated counts via views — individual rows are blocked by policy.reminders: workspace-scoped. Users see their own creations plus reminders targeting them.slack_installations: admin-only read.- The scheduler Edge Function uses the service-role client.
Anonymity guarantee
For anonymous responses, user_id is NULL by construction — no
update can set it. A CHECK constraint and a dedicated RLS policy
enforce this at the database layer:
create policy "anonymous_responses_stay_anonymous"
on responses for update
using (false)
with check (
(user_id is null) = (coalesce(
(select is_anonymous from surveys where id = responses.source_id),
false
))
);