Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ArnasDon/wacrm/llms.txt

Use this file to discover all available pages before exploring further.

Wacrm is a single Next.js application backed by Supabase and the Meta Cloud API. The stack is intentionally boring — widely understood, well-documented, and deployable on a single managed Node.js host with no Kubernetes or custom infrastructure. This page explains how the pieces fit together and where the important security boundaries are.

Stack overview

App layer

Next.js 16 with App Router. React Server Components handle data fetching and initial render; Client Components provide real-time UI (inbox, notifications, presence). TypeScript throughout. Tailwind v4 for styling.

Data layer

Supabase — Postgres with Row Level Security on every table; Supabase Auth (email/password); Supabase Storage (avatars, chat media); Supabase Realtime (WebSocket subscriptions for the inbox, notifications, and online presence).

WhatsApp layer

Meta Cloud API — the official WhatsApp Business API. Inbound messages and delivery status callbacks arrive at the webhook endpoint. Outbound messages are sent via Meta’s messages API from server-side routes.

AI layer

Direct provider API calls — OpenAI Chat Completions and Anthropic Messages, called from server-side routes only. Each account brings its own key (stored AES-256-GCM-encrypted in Supabase). No global provider key env var is required.

Key internal routes

These routes are not part of the public API — they are called by the Meta webhook delivery system, by internal cron jobs, or by the application itself.
RoutePurpose
POST /api/whatsapp/webhookReceives inbound WhatsApp messages and delivery status updates from Meta. Every POST is HMAC-SHA256 verified against META_APP_SECRET before any processing happens.
GET /api/whatsapp/webhookMeta webhook verification handshake (one-time setup).
POST /api/automations/engineProcesses automation trigger evaluation and step execution. Called internally when a triggering event occurs.
GET /api/automations/cronDrains pending Wait steps whose delay has elapsed. Must be called by an external scheduler on a regular interval — see Automations for cron setup.
GET /api/flows/cronFlow cron processing for time-based flow nodes.
GET /api/v1/* and POST /api/v1/*Public REST API endpoints, authenticated with API keys.

Security model

Wacrm layers several independent security controls so that a failure in one does not compromise the system. Token encryption. WhatsApp access tokens and AI provider keys are encrypted with AES-256-GCM before being stored in Supabase. The encryption key is ENCRYPTION_KEY — a 64-character hex string (32 bytes). Rotating this value orphans all previously encrypted tokens; users must re-save their keys in Settings after a rotation. Webhook verification. Every inbound POST to /api/whatsapp/webhook carries an X-Hub-Signature-256 header set by Meta. The handler verifies the HMAC-SHA256 signature against the raw request body using META_APP_SECRET and rejects any request that does not match. This prevents spoofed webhook deliveries. Row Level Security. Every Supabase table has RLS enabled. The is_account_member(account_id, min_role) SQL function is the single gatekeeper — every policy calls it, so no row can escape its account boundary regardless of how it is queried. The function is SECURITY DEFINER so policy evaluation never triggers recursive RLS on the profiles table. API key auth. Public API keys are stored as SHA-256 hashes. Authentication lookups use the Supabase service-role client (which bypasses RLS, since an API caller has no user session) to find the key by hash, then verify it is not revoked or expired. The service-role key is never exposed to client-side code. Rate limiting. The default in-memory rate limiter enforces 120 requests per minute per API key. The limiter uses a fixed-window counter stored in a Node.js Map.
The in-memory rate limiter is per-process. On a single-instance deployment (the typical Hostinger or Vercel setup) this works correctly. For multi-instance deployments — multiple Hostinger nodes, Vercel serverless fan-out, or any horizontally scaled setup — the in-process Map is not shared across instances, so the effective limit is multiplied by the number of instances. Swap the check implementation in src/lib/rate-limit.ts for a shared store (Redis, Upstash, or Cloudflare Durable Objects) without changing any call sites.
CSP headers. next.config.ts configures Content-Security-Policy-Report-Only, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy on every response. The CSP currently ships in report-only mode — flip the header key to Content-Security-Policy once you have confirmed no legitimate requests are being flagged in your browser console.

Deployment model

Wacrm runs as a single Next.js process. It is stateless — no in-process state is required between requests (except the rate-limiter Map, which is advisory). All persistence lives in Supabase. This means the application can be horizontally scaled by running multiple instances pointed at the same Supabase project, with the caveat noted above for the rate limiter. The recommended deployment is a single Hostinger Managed Node.js instance. This covers the vast majority of team sizes and keeps operations simple — no load balancer, no container orchestration, no Redis to manage.

Build docs developers (and LLMs) love