Skip to main content
Reference

API & Integrations

Regurai exposes three kinds of server-side surfaces. Pick the right one for the job.

SurfaceMechanismCallerAuth
Internal app logiccreateServerFnSame-origin React codeUser session (requireSupabaseAuth)
Public HTTP endpointTanStack server route under src/routes/api/public/*External services (webhooks, cron)Per-route signature verification
Isolated security primitiveSupabase Edge Function (Deno)Internal callers needing IP visibility, isolation, or service-role powerFunction-specific

Internal APIs (server functions)

Authoring convention (see server-side-modern knowledge):

// src/lib/something.functions.ts
import { createServerFn } from "@tanstack/react-start";
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";
import { z } from "zod";

export const doThing = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
  .handler(async ({ data, context }) => {
    const { supabase, userId } = context; // RLS scoped to user
    // ...
  });

Global middleware in src/start.ts:

  • errorMiddleware (request-level error envelope).
  • attachSupabaseAuth (function-level — adds the user's bearer token to every server fn RPC).

Without attachSupabaseAuth, protected server fns return 401.

External APIs / webhooks

Server routes under src/routes/api/public/* are exempt from the auth gate on published deployments. Every public route MUST verify caller identity before doing anything — usually via HMAC signature.

Canonical webhook shape:

// src/routes/api/public/webhook.ts
import { createFileRoute } from "@tanstack/react-router";
import { createHmac, timingSafeEqual } from "crypto";

export const Route = createFileRoute("/api/public/webhook")({
  server: {
    handlers: {
      POST: async ({ request }) => {
        const sig = request.headers.get("x-webhook-signature");
        const body = await request.text();
        const expected = createHmac("sha256", process.env.WEBHOOK_SECRET!)
          .update(body).digest("hex");
        if (!sig || !timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
          return new Response("Invalid signature", { status: 401 });
        }
        // ...
        return new Response("ok");
      },
    },
  },
});

Stable URLs for external configuration:

  • Production: project--<id>.lovable.app
  • Preview: project--<id>-dev.lovable.app

Edge functions catalogue

FunctionPathPurposeAuth model
login-rate-limitsupabase/functions/login-rate-limit/index.tsIP-keyed brute-force lockout (5/15 min)Anon; CORS open to app origin

To add an edge function:

  1. Scaffold under supabase/functions/<name>/index.ts.
  2. Use the service-role client only inside the function — never expose its key.
  3. Deploy via the Lovable Cloud edge-function tooling.
  4. Add the entry to the table above in this chapter.

Third-party integrations

IntegrationPurposeLocation
Supabase AuthIdentity, sessions, MFA@supabase/supabase-js, @/integrations/supabase/*
Supabase PostgresApplication dataRLS-scoped via server fns
Cloudflare WorkersEdge runtime / SSRBuilt-in to TanStack Start
Lovable AI GatewayLLM calls (where used)Preferred over direct provider keys

When adding a new third-party integration, prefer existing platform connectors before adding a custom API key. Document each new integration here with: what it does, what data is shared, what credential is required, and where the secret is stored.

Error handling standards

  • All server functions throw native Error or Response (401, 403, etc.).
  • Public routes return Response objects with a clear status code; never leak stack traces.
  • Client code surfaces server fn errors via TanStack Query's error state; user messaging is generic for auth failures.
  • All errors are logged to the platform's edge / SSR log stream; no PII in log messages.

Fairness decisions ingestion (Phase 5.3)

External AI systems push decision events into Regurai via:

POST /api/public/hooks/fairness-decisions
Headers:
  x-tenant-id: <uuid>
  x-signature: <hex sha256 hmac of raw body>
  x-secret-label: <optional secret label for rotation>
Body: { "decisions": [ DecisionEvent, ... ] }   // max 500 per batch, body ≤1 MB

Authentication is HMAC-SHA256 of the raw request body, using a per-tenant secret stored in public.fairness_ingest_secrets. The secret column is not readable by any authenticated role (column-level grants); only the backend service role can read it during verification. Admins create and rotate secrets through the (admin-only) RLS path; the plaintext is shown once at creation time and never again.

DecisionEvent schema (Zod-validated):

FieldTypeNotes
case_refstring (≤128)Required. Caller-side identifier.
subject_refstring (≤128)Optional. Caller-side identifier.
model_iduuidOptional. References public.ai_models.
decision_labelstring (≤64)Required.
decision_scorenumberOptional.
outcomeenumOne of: approved, declined, escalated, referred, pending.
protected_groupstringOptional. Must match the allow-list `^(age_band
reviewer_overridebooleanOptional.
latency_msint (0–600 000)Optional.
features_redactedobjectOptional. Must be pre-redacted; strings capped at 256 chars.
rationalestring (≤2 000)Optional.

Defence-in-depth: the entire payload is scanned for SSN / IBAN / email / credit-card shapes after Zod validation; matches cause HTTP 422 even if the caller smuggled them inside features_redacted strings.

On success the row is inserted with is_seed = false, tenant scoped by the matched secret's tenant_id — never by any tenant value in the body.

Out of scope for 5.3 and deferred to Phase 5.6 — Client AI Connectors: SDKs, batch/CSV import UI, Regurai-side polling, schema discovery, and real-time streaming.