# Supabase

[Supabase](https://supabase.com/) offers the best of both worlds — [storage buckets](https://supabase.com/docs/guides/storage) for raw payloads and [Postgres tables](https://supabase.com/docs/guides/database/tables) 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 page](https://dashboard.tryterra.co/dashboard/connections), 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](#data-structure) below)

No manual setup is required.

### Data Structure

#### Database tables

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

```sql
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:

```
{data_type}/{payload_id}.json
```

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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tryterra.co/health-and-fitness-api/integration-setup/setting-up-data-destinations/supabase.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
