Public
Remixed
WhatsApp messages to kanban board via Unipile API
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

WhatsApp Kanban

A cron job that fetches your WhatsApp messages via Unipile, uses Cerebras Inference (gpt-oss-120b) to extract real, actionable todos from the chatter, and organizes them on a kanban board — grouped by whatever project you map each client/chat to.

How it works

Rendering mermaid diagram...
  1. sync.ts (interval cron) — runs every 6 hours, fetches new WhatsApp messages from Unipile, groups unprocessed messages per chat, and sends each batch to Cerebras (gpt-oss-120b) with a strict JSON schema (response_format: json_schema) to pull out real tasks — ignoring small talk. Extracted todos are tagged with the project mapped to that chat (if any) and stored in the "Inbox" column.
  2. lib/cerebras.ts — Cerebras Inference client (OpenAI-compatible API) doing structured-output extraction: title, summary, priority, due date, and whether a message is actually actionable.
  3. lib/db.ts — SQLite schema: todos (inbox → todo → doing → done), a project_mappings table (chat → project), and a processed_messages table so chats without actionable content aren't re-sent to the LLM every sync.
  4. index.ts (HTTP) — serves the React kanban UI, todo CRUD routes, and project-mapping routes.
  5. Settings panel (⚙️ in the UI) — lists your WhatsApp chats/clients and lets you assign each one a project name.

Setup

1. Connect your WhatsApp account to Unipile

Follow the Unipile WhatsApp guide to connect your WhatsApp account. Note your account ID from the Unipile dashboard.

2. Get a Cerebras API key

Sign up at the Cerebras Cloud Console and create an API key. Todos are extracted using the gpt-oss-120b model via Cerebras's OpenAI-compatible API.

3. Set environment variables

Env varDescription
UNIPILE_DSNYour Unipile data source URL (e.g. https://xxx.unipile.com)
UNIPILE_API_KEYYour Unipile access token
UNIPILE_ACCOUNT_IDThe WhatsApp account ID linked in Unipile
CEREBRAS_API_KEYYour Cerebras Cloud API key (used for todo extraction)
BOARD_PASSPHRASEA passphrase to protect your kanban board (since the val is public on free tier)

👉 Add UNIPILE_DSN here: https://www.val.town/x/pc_style/whatsapp-kanban/environment-variables?key=UNIPILE_DSN 👉 Add UNIPILE_API_KEY here: https://www.val.town/x/pc_style/whatsapp-kanban/environment-variables?key=UNIPILE_API_KEY 👉 Add UNIPILE_ACCOUNT_ID here: https://www.val.town/x/pc_style/whatsapp-kanban/environment-variables?key=UNIPILE_ACCOUNT_ID 👉 Add CEREBRAS_API_KEY here: https://www.val.town/x/pc_style/whatsapp-kanban/environment-variables?key=CEREBRAS_API_KEY 👉 Add BOARD_PASSPHRASE here: https://www.val.town/x/pc_style/whatsapp-kanban/environment-variables?key=BOARD_PASSPHRASE

4. Map clients/chats to projects

Open the board, click ⚙️ Settings, and give each WhatsApp chat a project name. Any todos extracted from that chat going forward will be tagged with that project. Use the project filter dropdown on the board to focus on one project at a time.

5. Test the sync

Run sync.ts manually from the Val Town editor to pull your first batch of messages and extract todos.

File structure

sync.ts                        ← Cron job (every 6h) — fetches messages, runs LLM extraction
index.ts                       ← Hono server — serves UI + API routes
lib/
  db.ts                        ← SQLite schema + todo/project-mapping CRUD helpers
  unipile.ts                   ← Unipile API client (messages + chats)
  cerebras.ts                  ← Cerebras (gpt-oss-120b) structured-output extraction
frontend/
  index.html                   ← HTML shell
  index.tsx                    ← React entrypoint
  components/
    App.tsx                    ← Kanban board UI (project badges, filter, settings launcher)
    Settings.tsx                ← Client → project mapping panel

API endpoints

MethodPathDescription
GET/api/todosList all todos
PATCH/api/todos/:idMove a card ({ status: "inbox|todo|doing|done" })
DELETE/api/todos/:idDelete a card
GET/api/chatsList WhatsApp chats from Unipile (for the mapping UI)
GET/api/project-mappingsList chat → project mappings
PUT/api/project-mappings/:chatIdSet/update a mapping ({ project_name, chat_name })
DELETE/api/project-mappings/:chatIdRemove a mapping