> ## 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.
