> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clix.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Clix + Supabase Integration Guide

> How to send Clix push notifications from Supabase using Edge Functions, SQL triggers, and helper builders.

# Clix + Supabase Integration

This guide shows how to integrate [Clix](https://clix.so) push notifications into a Supabase project using **Edge Functions** and Postgres triggers. You will:

* Add a reusable `ClixClient` inside `supabase/functions/_shared`
* (Recommended) Create helper builders for `ClixNotification` payloads
* Deploy Edge Functions that send notifications
* Optionally wire automatic dispatch via database triggers with `pg_net`

<Note>
  Supabase Edge Functions run in Deno on the Supabase infrastructure and can
  securely access secrets you store with `supabase secrets`. See the official
  docs: **Edge Functions** – [https://supabase.com/docs/guides/functions](https://supabase.com/docs/guides/functions)
</Note>

## 1. Prerequisites

* A Supabase project (with CLI installed) – [https://supabase.com/docs/guides/cli](https://supabase.com/docs/guides/cli)
* A Clix project (obtain Project ID + Secret API Key)
* Supabase CLI authenticated: `supabase login`
* (If using triggers) Postgres extension `pg_net` enabled (we'll cover this below)

## 2. Configure Secrets / Environment Variables

Store credentials as Supabase Function secrets (never hard‑code keys):

```bash Set Supabase Edge Function secrets theme={null}
supabase secrets set \
  CLIX_PROJECT_ID=your_clix_project_id \
  CLIX_API_KEY=your_clix_secret_api_key \
  CLIX_API_BASE=https://api.clix.so

# Existing required Supabase vars (examples) – ensure already set
supabase secrets set \
  SUPABASE_URL=https://your-project-ref.supabase.co \
  SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
```

Docs:

* Secrets: [https://supabase.com/docs/guides/functions/secrets](https://supabase.com/docs/guides/functions/secrets)
* Environment access in Deno: [https://supabase.com/docs/guides/functions#environment-variables](https://supabase.com/docs/guides/functions#environment-variables)

## 3. Add the Shared Clix Client

Create the file below at `supabase/functions/_shared/clix_client.ts`.

```ts supabase/functions/_shared/clix_client.ts theme={null}
// Lightweight reusable Clix notification client for Deno Edge Functions
// Uses project_user_id targeting instead of device_id

export interface ClixNotification {
  target: { project_user_id: string };
  title: string;
  body: string;
  image_url?: string;
  landing_url?: string;
}

export interface ClixResult {
  delivery_results?: unknown;
  [k: string]: unknown;
}

export class ClixClient {
  private projectId: string;
  private apiKey: string;
  private base: string;
  constructor(opts?: {
    projectId?: string;
    apiKey?: string;
    baseUrl?: string;
  }) {
    this.projectId = opts?.projectId || Deno.env.get("CLIX_PROJECT_ID") || "";
    this.apiKey = opts?.apiKey || Deno.env.get("CLIX_API_KEY") || "";
    this.base = (
      opts?.baseUrl ||
      Deno.env.get("CLIX_API_BASE") ||
      "https://api.clix.so"
    ).replace(/\/$/, "");
    if (!this.projectId || !this.apiKey) {
      throw new Error("ClixClient missing credentials");
    }
  }

  async send(
    notifications: ClixNotification[]
  ): Promise<{ ok: boolean; result: ClixResult; status: number }> {
    const res = await fetch(`${this.base}/api/v1/messages:send`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Clix-Project-ID": this.projectId,
        "X-Clix-API-Key": this.apiKey,
      },
      body: JSON.stringify({ push_notifications: notifications }),
    });
    const json: ClixResult = await res.json().catch(() => ({}));
    return { ok: res.ok, status: res.status, result: json };
  }
}
```

## 4. (Recommended) Add Scenario Helper Builders

Instead of hand‑crafting notification objects each time, centralize builders for consistent structure and future enrichment.

```ts supabase/functions/_shared/notification_scenarios.ts theme={null}
import { ClixNotification } from "./clix_client.ts";

// Multiple recipients for a completed todo
export function buildTodoCompletedNotifications(
  projectUserIds: string[],
  todo: { id: number; title: string }
): ClixNotification[] {
  return projectUserIds.map((pid) => ({
    target: { project_user_id: pid },
    title: "Todo Completed",
    body: todo.title || `Todo #${todo.id} was completed`,
    landing_url: `https://example.com/todos/${todo.id}`,
  }));
}
```

<Note>
  These helpers can evolve to insert A/B test metadata, analytics tags,
  localization, etc. Centralizing keeps Edge Function logic lean.
</Note>

## 5. Edge Function: Notify Todo Completion

Minimal example: accept a `todo_id` and a target `project_user_id`, build a notification, and send it via `ClixClient` (no database lookup logic included here).

```ts supabase/functions/notify-todo/index.ts theme={null}
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import {
  ClixClient,
  buildTodoCompletedNotification,
} from "../_shared/clix_client.ts";

// Minimal: requires env secrets (CLIX_PROJECT_ID, CLIX_API_KEY)
serve(async (req: Request) => {
  if (req.method !== "POST") return json({ error: "Only POST" }, 405);
  const { todo_id, project_user_id, title } = await req
    .json()
    .catch(() => ({}));
  if (!todo_id) return json({ error: "todo_id required" }, 400);
  if (!project_user_id) return json({ error: "project_user_id required" }, 400);

  // Build a minimal todo shape for the helper (no DB fetch)
  const todo = { id: todo_id, title: title || "" };
  const clix = new ClixClient();
  const notification = buildTodoCompletedNotification(project_user_id, todo);
  const { ok, result, status } = await clix.send([notification]);
  if (!ok) return json({ error: "clix send failed", details: result }, status);
  return json({ ok: true, sent: 1, clix: result });
});

function json(data: unknown, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { "Content-Type": "application/json" },
  });
}
```

### Deploy the Function

```bash theme={null}
supabase functions deploy notify-todo --no-verify-jwt
```

(You can require JWT if only your backend calls this. See auth function docs: [https://supabase.com/docs/guides/functions/auth](https://supabase.com/docs/guides/functions/auth))

## 6. Calling the Edge Function (Invocation Options)

You can integrate `notify-todo` in three primary ways—choose based on coupling, complexity, and operational preferences. (Code snippets intentionally omitted.)

1. Database Trigger (pg\_net): Attach a lightweight trigger to the `todos` table so when a row transitions to `completed`, Postgres performs an HTTP POST to the deployed Edge Function. Pros: automatic, low latency, no extra app call. Cons: trigger logic inside SQL, needs `pg_net` and careful key handling.
2. Direct API Call: From your backend (or another Edge Function) perform an authenticated POST with the `todo_id`. Pros: simplest to reason about in application code, easier to add conditional logic or retries. Cons: requires your app layer to explicitly invoke for every event.
3. Scheduled / Batch Orchestrator: Periodic job (cron, scheduled function, worker) queries for recently completed todos and sends notifications in batches (reduces per-event overhead). Pros: efficient for high volume; can deduplicate. Cons: not real-time; added scheduling complexity.

Selection guidance:

* Want instant, automatic dispatch on state change? Use a trigger.
* Need business rules (feature flags, throttling, analytics gating)? Use direct API call.
* Need aggregation (daily digests, rate limiting)? Use scheduled batch.

## 7. Testing the Flow

1. Insert / update a `todo` to status `completed`
2. Confirm `notification_dispatch_log` entries
3. Verify successful HTTP responses & Clix dashboard reception
4. For manual test call the function:

```bash theme={null}
curl -X POST \
  https://<project-ref>.functions.supabase.co/notify-todo \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SUPABASE_SERVICE_ROLE_KEY" \
  -d '{"todo_id": 123}'
```

## 8. Security & Best Practices

* Put your Clix Secret API Key only in Supabase secrets (never ship to clients)
* Prefer trigger-based automation for reliability; fall back to client calls when immediacy or dynamic logic is required
* Use structured builders to evolve payloads without touching every function
* Log responses (already in examples) for observability; consider adding retention policies

## 9. Useful Supabase Docs

* Edge Functions: [https://supabase.com/docs/guides/functions](https://supabase.com/docs/guides/functions)
* Secrets: [https://supabase.com/docs/guides/functions/secrets](https://supabase.com/docs/guides/functions/secrets)
* Supabase JS client: [https://supabase.com/docs/reference/javascript/start](https://supabase.com/docs/reference/javascript/start)
* Database Functions & Triggers: [https://supabase.com/docs/guides/database/functions](https://supabase.com/docs/guides/database/functions)
* RLS: [https://supabase.com/docs/guides/auth/row-level-security](https://supabase.com/docs/guides/auth/row-level-security)
* `pg_net` Extension: [https://supabase.com/docs/guides/database/extensions/pg\_net](https://supabase.com/docs/guides/database/extensions/pg_net)

## 10. Summary

You now have:

* A reusable `ClixClient` consuming environment secrets
* Scenario helpers for consistent notifications
* Edge Functions (`notify-todo`) to send messages
* Optional Postgres triggers to automatically invoke those functions

Iterate by adding more scenario builders (e.g. silent data sync, milestone achievements) and enrich payloads with custom analytics fields.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.