Supabase

Supabasearrow-up-right offers the best of both worlds — storage bucketsarrow-up-right for raw payloads and Postgres tablesarrow-up-right for structured metadata, all within the same platform.

Setup

Terra integrates with Supabase via OAuth. Everything is provisioned automatically:

  1. In the Terra Dashboard > Connections pagearrow-up-right, click "Add New" and select Supabase.

  2. You'll be redirected to Supabase to authorize Terra.

  3. Select an existing project or create a new one.

  4. Terra automatically provisions the following in your project:

    • A terra-payloads storage bucket for raw data files

    • Access via your project's service role key for secure write operations

    • Three database tables (see Data Structure below)

No manual setup is required.

Data Structure

Database tables

Terra creates three tables in your Supabase project's Postgres database:

CREATE TABLE terra_users (
    user_id TEXT PRIMARY KEY,
    reference_id TEXT,
    created_at TIMESTAMPTZ,
    granted_scopes TEXT,
    provider TEXT,
    state TEXT
);

CREATE TABLE terra_data_payloads (
    payload_id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL,
    data_type TEXT,
    created_at TIMESTAMPTZ,
    start_time TIMESTAMPTZ,
    end_time TIMESTAMPTZ,
    FOREIGN KEY (user_id) REFERENCES terra_users(user_id)
        ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE terra_misc_payloads (
    payload_id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL,
    data_type TEXT,
    payload_type TEXT,
    created_at TIMESTAMPTZ,
    FOREIGN KEY (user_id) REFERENCES terra_users(user_id)
        ON DELETE CASCADE ON UPDATE CASCADE
);
  • terra_users — Created/updated on authentication events. The state column tracks the user's lifecycle (active, deauth, user_reauth, etc.).

  • terra_data_payloads — One row per data event (activity, sleep, body, etc.). Same-day data for the same user and data type is deduplicated (last write wins).

  • terra_misc_payloads — Non-data events (deauth, access revoked, etc.).

Foreign keys use ON DELETE CASCADE and ON UPDATE CASCADE, so deleting or updating a user automatically propagates to their payloads.

Storage bucket

Raw JSON payloads are uploaded to the terra-payloads bucket with the path:

For example: activity/3a4b5c6d7e8f9012.json

Row Level Security (RLS)

RLS is disabled on the Terra tables by default. This is intentional:

  • Terra writes data using a service role key, which bypasses RLS regardless.

  • Enabling RLS without policies would block your client-side reads (Supabase defaults to deny-all when RLS is on with no policies).

You're free to enable RLS and add your own policies to control how your frontend or users access the data. Terra's writes will continue to work regardless.

User lifecycle handling

Terra automatically manages user state transitions in your Supabase tables:

  • Re-authentication — When a user re-authenticates, Terra migrates their existing payloads to the new user ID and updates all foreign key references.

  • Deauthentication — When a user disconnects, Terra deletes their record from terra_users (cascading to all payload rows) and removes their files from the storage bucket.

Last updated

Was this helpful?