Documentation
Self-host wup end to end

Everything you need to take the template from a fresh fork to a production deploy. Work through the pages in order, or jump to the one you need.

Setup

Getting started

Fork, install, run the template locally in about 15 minutes.

Read →

Supabase setup

Create the database, apply migrations, grab the keys.

Read →

WhatsApp setup

Meta app, access token, webhook verification.

Read →

Environment variables

Full reference for every env var the app reads.

Read →
Deploy

Deploy on Hostinger

Ship the template on Hostinger Managed Node.js Hosting.

Read →

Automations cron

Schedule the drain so Wait steps resume on time.

Read →
Features

Inbox

Shared workspace for WhatsApp conversations — real-time threads, templates, reactions.

Read →

Contacts

Address book — manual add, CSV import, tags, contact detail view.

Read →

Pipelines

Kanban deal boards with drag-drop stages and win-rate analytics.

Read →

Templates

Build, submit, and edit WhatsApp message templates with real-time Meta approval.

Read →

Broadcasts

Bulk-send approved templates to a targeted audience with delivery tracking.

Read →

Automations

Event-driven step chains — keyword auto-replies, lead routing, webhook fan-out.

Read →

Flows

Branching, button-driven WhatsApp conversations — no-code chatbots with handoff.

Read →

Settings

Profile + password, WhatsApp connection, tag library, color theme picker.

Read →

Members

Invite teammates, role-based access, ownership transfer.

Read →
Reference

Architecture

Stack, folder layout, request lifecycle.

Read →

Troubleshooting

The usual suspects when something breaks.

Read →
Getting started
Fork, install, run the template locally in about 15 minutes.

Prerequisites

  • Node.js 18+ — the repo targets the current LTS.
  • npm — ships with Node.
  • A free Supabase project (see Supabase setup).
  • A Meta WhatsApp Business account (see WhatsApp setup).

1 — Fork & clone

# Fork on GitHub, then: git clone https://github.com/<you>/wup.git cd wup

2 — Install dependencies

npm install

3 — Create .env.local

Copy the example and fill in the blanks — see Environment variables for the full reference.

cp .env.example .env.local

4 — Apply Supabase migrations

Run the SQL files under supabase/migrations/ against your Supabase project. Each file is numbered; apply them in order via the SQL Editor or the Supabase CLI.

5 — Start the dev server

npm run dev

Open http://localhost:3000 — sign up with email + password, then head to Settings → WhatsApp to paste your credentials.

💡 Tip Most teams are live in under 30 minutes once their WhatsApp Business number has been approved by Meta.
Supabase setup
Create the database, apply migrations, grab the keys.

1 — Create a Supabase project

Go to supabase.com → New project. Pick a region close to your users. The free tier is fine for development.

2 — Grab your keys

In your Supabase dashboard, go to Settings → API. You need:

KeyWhere to find it
NEXT_PUBLIC_SUPABASE_URLProject URL — starts with https://
NEXT_PUBLIC_SUPABASE_ANON_KEYanon / public key
SUPABASE_SERVICE_ROLE_KEYservice_role key (keep secret)

3 — Run migrations

Open the SQL Editor in Supabase and run each file from supabase/migrations/ in numeric order. Each migration is idempotent — safe to re-run.

4 — Enable email auth

Go to Authentication → Providers → Email. Make sure it's enabled. For development, you can disable "Confirm email"; for production, configure a custom SMTP provider.

5 — Set up Storage (optional)

Create a avatars bucket for profile pictures. Set it to public.

💡 Tip Use the Supabase CLI for automated migration management: npx supabase db push
WhatsApp setup
Meta app, access token, webhook verification.

1 — Create a Meta App

Go to developers.facebook.com → My Apps → Create App → Business → WhatsApp.

2 — Get your credentials

From WhatsApp → API Setup grab:

  • Phone number ID — your WhatsApp Business phone number identifier.
  • WhatsApp Business Account ID
  • Temporary access token — for testing. Generate a permanent System User token for production.

3 — Configure the webhook

Under WhatsApp → Configuration → Webhook:

  • Callback URL: https://your-domain.com/api/whatsapp/webhook
  • Verify token: the value you set in Settings → WhatsApp inside wup.
  • Subscribe to: messages

4 — Paste into wup

Open Settings → WhatsApp in your running wup instance. Paste the phone number ID, access token, and verify token. The app encrypts them at rest using AES-256-GCM.

5 — Get the App Secret

Go to Meta → App Settings → Basic → App Secret. Set it as META_APP_SECRET in your env — it's required for webhook signature verification.

⚠️ Important Without META_APP_SECRET, every inbound webhook POST will get a 401. Double-check the value matches the App Secret exactly.
Environment variables
Full reference for every env var the app reads.

Copy .env.example to .env.local and fill in the values. Variables prefixed NEXT_PUBLIC_ are exposed to the browser — everything else stays server-side.

VariableRequiredDescription
NEXT_PUBLIC_SUPABASE_URLYour Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEYSupabase anon / public key
SUPABASE_SERVICE_ROLE_KEYSupabase service_role key — never expose to the client
ENCRYPTION_KEY64 hex chars for AES-256-GCM encryption of WhatsApp tokens
META_APP_SECRETMeta App Secret for webhook signature verification
NEXT_PUBLIC_SITE_URLYour production URL (e.g. https://crm.example.com)
CRON_SECRETRecommendedSecret token to protect the automation cron endpoint

Generating ENCRYPTION_KEY

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
⚠️ Important NEXT_PUBLIC_* values are baked into the client bundle at build time. After changing any of them, you must re-run npm run build.
Deploy on Hostinger
Ship the template on Hostinger Managed Node.js Hosting.

Why Hostinger?

Managed Node.js Hosting is the recommended deployment target. Connect your forked repo and the CRM is live in about 30 seconds. No servers to patch, automatic SSL, and git-push deploys.

1 — Purchase hosting

Get a Managed Node.js Hosting plan from Hostinger. Any tier works.

2 — Connect your repo

In hPanel, go to Website → Node.js and connect your GitHub/GitLab repository (the forked wup repo).

3 — Set environment variables

Add all required env vars from the Environment variables section in the hPanel env configuration.

4 — Build & deploy

  • Build command: npm run build
  • Start command: npm start
  • Node version: 18+

Hostinger auto-deploys on every push to your main branch.

5 — Point your domain

Attach your custom domain in hPanel. SSL is provisioned automatically. Update NEXT_PUBLIC_SITE_URL and Supabase redirect URLs to match.

6 — Configure the webhook

Update Meta's webhook callback URL to your new production domain: https://your-domain.com/api/whatsapp/webhook

💡 Tip After a domain change, Hostinger provisions a new SSL certificate — Meta's webhook subscription may need to be re-verified.
Automations cron
Schedule the drain so Wait steps resume on time.

Automations with Wait steps need an external pinger to drain the queue. The app exposes GET /api/automations/cron — hit it on a schedule and pending steps will resume.

How it works

When a Wait step's timer expires, the execution stays in a waiting state until the cron endpoint is called. The endpoint checks for due executions, resumes them, and returns {"processed": N}.

Securing the endpoint

Set CRON_SECRET in your env. Pass it as a header:

curl -H "x-cron-secret: your-secret" https://crm.example.com/api/automations/cron

Recommended schedule

Every 1 minute for near-real-time waits, or every 5 minutes if precision isn't critical. Use any external cron service — UptimeRobot (free), cron-job.org, or a simple server-side crontab.

💡 Tip Quick sanity check: curl -s -H "x-cron-secret: <secret>" https://crm.example.com/api/automations/cron — if it returns {"processed": N}, the endpoint is working.
Inbox
Shared workspace for WhatsApp conversations — real-time threads, templates, reactions, quote-replies.

The Inbox is the main workspace. Every WhatsApp conversation lands here in real time via Supabase Realtime. The left column lists threads; clicking one opens the full message stream on the right.

Key capabilities

  • Real-time messaging — incoming and outgoing messages appear instantly via Supabase Realtime subscriptions.
  • Agent assignment — assign conversations to specific team members or round-robin across the team.
  • Template sending — pick any Meta-approved template directly from the composer and fill in variables inline.
  • Reactions & quote-replies — react to messages or reply to a specific message in context.
  • Internal notes — leave notes only your team sees, attached to any conversation.
  • Media support — send and receive images, videos, documents, audio, and location messages.
  • Contact sidebar — see the contact's profile, tags, custom fields, deals, and history without leaving the thread.
  • Search & filter — full-text search across conversations, filter by agent, status (open/closed), or tags.
  • Unread indicators — unread count badges on the sidebar so urgent replies never slip through.

24-hour window

WhatsApp Business API enforces a 24-hour customer service window. You can reply freely within 24 hours of the customer's last message. After that, you must use an approved template to re-open the conversation.

Contacts
Address book — manual add, CSV import, tags, contact detail view, deletion behaviour.

The contact hub is your team's address book. Contacts are created automatically when a new WhatsApp number messages you, or you can add them manually or via CSV import.

Features

  • Manual add — create contacts with name, phone number, email, and notes.
  • CSV import — bulk import contacts from a spreadsheet. The importer maps columns automatically.
  • Tags — colour-coded labels for segmenting contacts. Create and manage tags from Settings → Tags.
  • Custom fields — add any extra data you need (company, location, etc.).
  • Automatic deduplication — contacts are matched by phone number. Duplicates are merged.
  • Contact detail view — see all conversations, deals, tags, notes, and custom fields in one place.
  • Deletion — deleting a contact removes them from the list but preserves conversation history.
Pipelines
Kanban deal boards with drag-drop stages, multi-pipeline support, and win-rate analytics.

Pipelines let you track deals through custom stages — from first contact to closed-won. Every deal is linked to a contact and keeps its WhatsApp thread one click away.

Features

  • Multiple pipelines — separate pipelines for different sales processes (e.g., inbound leads vs. partnerships).
  • Custom stages — add, rename, reorder, and delete stages per pipeline.
  • Kanban board — drag deals between stages. Touch-friendly on mobile.
  • Deal value — attach a monetary value. Totals per stage and pipeline-wide are shown automatically.
  • Linked contacts — every deal connects to a contact. Click through to their full profile.
  • Notes & activity — add notes to deals, see when they were created, moved, or won.
  • Won / lost tracking — mark deals as won or lost. Analytics show your conversion rate.
Templates
Build, submit, edit, and delete WhatsApp message templates in-app — with real-time approval status from Meta.

WhatsApp requires pre-approved templates for any message sent outside the 24-hour service window. wup lets you build, submit, and manage templates directly in the app.

Template builder

  • Header — optional text, image, video, or document header.
  • Body — the main message text. Supports variables like {{1}}, {{2}}, etc.
  • Footer — optional footer text.
  • Buttons — up to 3 buttons: quick reply, URL, or phone number.

Approval workflow

When you submit a template, Meta reviews it (usually within minutes). The approval status syncs back automatically: Approved, Rejected, or Pending. Only approved templates can be used in the inbox, broadcasts, and automations.

Sync

Templates created elsewhere (e.g., directly in the Meta Business Suite) are synced into wup automatically. The app polls Meta's template API and keeps your local list up to date.

Broadcasts
Bulk-send approved templates to a targeted audience. 4-step wizard, per-recipient delivery tracking.

Broadcasts let you send the same approved template to many contacts at once — think product announcements, promotions, or re-engagement campaigns.

4-step wizard

  1. Choose a template — pick from your approved templates.
  2. Fill variables — set the variable values (same for all recipients, or use contact fields for personalization).
  3. Select audience — filter contacts by tags, or pick them individually.
  4. Review & send — preview the final message and confirm.

Delivery tracking

Each broadcast shows per-recipient status: sent, delivered, read, or failed. Summary stats (delivery rate, read rate) update in real time.

⚠️ WhatsApp policy Only send broadcasts to contacts who have opted in. Excessive spam reports can get your number banned by Meta.
Automations
Event-driven step chains — keyword auto-replies, lead routing, tag-based reactions, webhook fan-out.

Automations let you build event-driven workflows that react to WhatsApp events. Each automation has one trigger and one or more steps that execute in sequence.

Triggers

  • New message — fires when any message is received.
  • New contact — fires when a new phone number messages you for the first time.
  • Keyword match — fires when an incoming message contains specific keywords.
  • Tag added/removed — fires when a contact's tags change.
  • Schedule — fires on a time-based schedule.

Step types

  • Send template — send an approved WhatsApp template.
  • Send message — send a free-form reply (within the 24-hour window).
  • Add/remove tag — modify the contact's tags.
  • Create deal — add a deal to a pipeline.
  • Assign agent — assign the conversation to a specific team member.
  • Wait — pause execution for a set duration (requires the cron endpoint).
  • Condition — branch based on tag, contact field, or keyword.
  • Webhook — POST data to an external URL.

Execution logs

Every automation run is logged with timestamps, step results, and any errors. View logs from the automation detail page.

Flows
Branching, button-driven WhatsApp conversations — no-code chatbots with handoff.

Flows let you build interactive, button-driven conversational experiences. Think of them as decision trees — each node sends a message with buttons, and the contact's response determines the next step.

How they work

  • Start node — the entry point. Triggered when a contact enters the flow (via automation or manual trigger).
  • Message nodes — send a text message with up to 3 quick-reply buttons.
  • Branch on reply — route to different paths based on which button the contact taps.
  • Action nodes — add tags, create deals, assign agents, or fire webhooks mid-flow.
  • Handoff — exit the flow and transfer control to a human agent in the inbox.

Use cases

  • Qualify leads with a quick survey before routing to sales.
  • Let customers self-serve (check order status, FAQ, booking).
  • Build an onboarding sequence that adapts based on responses.
Settings
Profile + password, WhatsApp connection, tag library, color theme picker.

Settings is split into sections accessible from the sidebar:

Profile

Update your name, avatar (stored in Supabase Storage), and password. Per-user settings are always editable regardless of your role.

WhatsApp

Paste your Phone Number ID, Access Token, and Verify Token. The app encrypts tokens at rest using AES-256-GCM. Reconnect anytime if your credentials change.

Tags

Create, rename, recolour, and delete tags. Tags are account-wide — they appear in contacts, conversations, broadcasts, and automations.

Templates

Manage your WhatsApp message templates — same as the Templates page, accessible from settings for convenience.

Theme

Pick a colour theme for the app UI. Preferences are saved per-user.

Members
Invite teammates, role-based access, ownership transfer.

The Members surface turns a solo wup install into a shared team workspace. Invite teammates by link, give each one a role, hand off ownership, and remove people — all from Settings → Members.

Roles

Four roles in a flat privilege ladder. Each inherits everything below it:

RoleCan do
OwnerEverything. Exactly one per account. Plus: transfer ownership and delete the account.
AdminManage members (invite / remove / change roles) and edit account-wide settings.
AgentSend messages, create/edit contacts, move deals, run broadcasts, build automations and flows.
ViewerRead-only across the whole app. Sees everything, changes nothing.

Inviting members

Go to Settings → Members → Invite. An invite link is generated — share it with your teammate. They sign up through the link and join your account with the role you chose.

💡 Note Multi-user requires migrations 017–020. If you're self-hosting and upgrading, apply them before deploying.
Architecture
Stack, folder layout, request lifecycle.

One-page tour of the codebase — so you know where to look when you want to change something in your fork.

Stack

LayerToolWhy
RenderingNext.js 16 (App Router)Server components for data-fetch pages; client components where interactivity's needed. React 19.
UITailwind v4 + shadcnZero-runtime styling, composable dark-theme-first components.
Data + AuthSupabasePostgres with Row-Level Security, built-in email/password auth, Storage, Realtime.
WhatsApp®Meta Cloud APIOfficial Business API. No third-party gateway.
Encryptionnode:crypto AES-256-GCMPer-user WhatsApp access + verify tokens at rest.
SchedulerExternal HTTP pingerHits GET /api/automations/cron to drain Wait-step executions.

No ORM, no GraphQL layer, no dedicated backend. The Next server-side routes read and write Supabase directly via @supabase/ssr.

Folder layout

wup/ ├─ src/ │ ├─ app/ Next.js App Router routes │ │ ├─ (auth)/ login, signup, forgot-password │ │ ├─ (dashboard)/ authenticated UI │ │ │ ├─ dashboard/ home / metrics / activity feed │ │ │ ├─ inbox/ shared inbox │ │ │ ├─ contacts/ contacts + tags │ │ │ ├─ pipelines/ Kanban deals │ │ │ ├─ broadcasts/ campaign list + builder │ │ │ ├─ automations/ flow builder + logs │ │ │ └─ settings/ Profile / WhatsApp / Tags │ │ ├─ api/ JSON endpoints (server-only) │ │ │ ├─ whatsapp/ webhook, send, broadcast, config │ │ │ └─ automations/ engine + cron + CRUD │ ├─ components/ UI components by feature │ ├─ hooks/ use-auth, use-realtime, etc. │ ├─ lib/ │ │ ├─ supabase/ client, server, middleware │ │ ├─ whatsapp/ Meta API client, encryption │ │ └─ automations/ engine, steps, validation ├─ supabase/ │ └─ migrations/ numbered SQL migration files ├─ public/ static assets └─ .env.example template for environment variables

Request lifecycle

Inbound: Meta → POST /api/whatsapp/webhook → verify signature → parse payload → upsert contact → insert message → broadcast via Supabase Realtime → inbox updates live.

Outbound: Composer → POST /api/whatsapp/send → decrypt token → call Meta API → insert message → broadcast via Realtime.

Troubleshooting
The usual suspects when something breaks.

Build-time

Error: ENCRYPTION_KEY must be 64 hex chars

ENCRYPTION_KEY is missing or not the right length. Generate one:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Error: supabaseUrl is required

NEXT_PUBLIC_SUPABASE_URL or NEXT_PUBLIC_SUPABASE_ANON_KEY is not set at build time. Make sure the env vars are exported before npm run build runs.

Auth

Confirmation email never arrives

In Supabase → Authentication → Providers → Email, either configure a custom SMTP provider (recommended for production) or temporarily turn off "Confirm email" while testing.

Password-reset links point at localhost:3000

Set NEXT_PUBLIC_SITE_URL and add the same origin to Supabase → Authentication → URL Configuration → Redirect URLs.

WhatsApp®

Webhook verification fails in Meta

  • The verify token in Meta must match exactly what you saved in Settings → WhatsApp inside the app.
  • The callback URL must be reachable without auth.
  • META_APP_SECRET must be set on the deployment.

Messages go out but nothing comes in

  • Confirm the webhook is Subscribed to messages under Meta → WhatsApp → Configuration.
  • Confirm the phone number is listed under Recipients during testing.
  • Check server logs — inbound webhooks hit POST /api/whatsapp/webhook.

Token decryption failed

ENCRYPTION_KEY changed since the token was saved. Reconnect from Settings → WhatsApp to re-encrypt with the current key.

Automations

Wait steps never resume

The cron drain is not running. See the Automations cron section above.

Deploy

502 Bad Gateway from Hostinger

  • Confirm the app status is Started in hPanel.
  • Check the app log viewer for a crash on boot — almost always a missing env var.
  • Confirm the startup command is npm start and that .next/ exists.

Env var changes don't take effect in the browser

NEXT_PUBLIC_* values are baked into the client bundle at build time. Re-run npm run build and restart the app.