Loading
ARK47 CRM is a shared team inbox for the WhatsApp Business API. Hand a conversation to a colleague, answer with a template Meta already approved, and watch the contact record write itself — while every workspace stays separated inside Postgres rather than by a WHERE clause somebody remembered to add.
whatsapp business api·approved templates·rls: forced
Hi — has my order shipped yet? 4471
Hi Layla, your order 4471 left our warehouse this morning. Track it any time with PT449102.
Perfect — thank you!
links 0·refused 0 this frame
The field behind this page is three workspaces — yours in teal, two others in off-white. A link only forms between two nodes in the same one, so when the clouds drift through each other nothing joins them; every crossing pair in range is refused and counted. It is a drawing of the rule your data actually runs under.
Nothing here is a module you switch on later. This is what happens to a single inbound WhatsApp message, in the order it happens.
Connect a WhatsApp Business number through Meta in a single pass. There is no second dashboard to go and configure afterwards.
Every message on that number arrives in one inbox your whole team can open. Not forwarded, not a copy — the thread itself, with an owner anyone can hand on.
The record is built out of the conversation. Names, numbers and history stay current because talking to someone is what updates them.
Reply in the thread, or send a message template you have already cleared with Meta — the only way to open a conversation outside the 24-hour window.
Most multi-tenant products keep customers apart with a condition bolted onto every query, and stay correct only as long as every query remembers. Here the separation lives in Postgres: a policy on the table decides what a connection may see, and the table enforces it.
-- apps/backend/prisma/rls.sql · all 66 tenant tablesALTER TABLE "public"."conversation" ENABLE ROW LEVEL SECURITY;ALTER TABLE "public"."conversation" FORCE ROW LEVEL SECURITY;CREATE POLICY tenant_isolation ON "public"."conversation" USING ("_ark_workspace_id" = current_setting('app.workspace_id', true)) WITH CHECK ("_ark_workspace_id" = current_setting('app.workspace_id', true));
66 / 66 tenant tables
Every connection is opened with your workspace stamped on it, and the database refuses to return — or accept — a row belonging to anyone else. A query that forgets to filter by workspace returns nothing, rather than somebody else's messages.
Create a workspace, connect a number through Meta, and your first shared thread is open. Everything above is what you get on the first day.