PRD: WhatsApp Workflow Builder POC
Problem Statement
Users need a way to build WhatsApp-native workflows visually, without writing code, so they can react to WhatsApp messages, use AI, send replies, and store structured local data. The first use case is a WhatsApp-driven task manager where a user can send commands like /task buy milk, have AI extract structured data, store it in a local table, and receive a confirmation message.
The POC must prove the workflow model, execution boundary, AI tool composition, and WhatsApp send/fetch integration without introducing a database yet.
Solution
Build a local-first workflow builder using React Flow. Users create workflows made of WhatsApp triggers, action blocks, AI Generate blocks, table action blocks, and condition blocks. Workflow definitions and local table state are stored in browser localStorage as JSON.
Runs execute through a server route handler. The client posts the current workflow JSON, a simulated WhatsApp message, local table state, and run mode to the runner API. The runner evaluates triggers, executes the graph, calls AI SDK for Generate blocks, runs AI tool block chains when called, applies table mutations as patches, and returns run output for the client to display and persist locally.
The POC includes a sidebar Test Chat that simulates WhatsApp messages against the current workflow. Send Message blocks are dry-run by default and produce outbound message intents. In live mode, send-message blocks call WasenderAPI through server-side route handlers.
User Stories
- As a workflow builder user, I want to create a workflow from a visual canvas, so that I can automate WhatsApp interactions without writing code.
- As a workflow builder user, I want all triggers to originate from WhatsApp, so that the product stays focused on WhatsApp workflows.
- As a workflow builder user, I want to add a WhatsApp command trigger, so that a message like
/task buy milk can start a workflow.
- As a workflow builder user, I want command definitions to reject spaces and invalid formats, so that command behavior is predictable.
- As a workflow builder user, I want command matching to require the command as the first token, so that casual messages containing a command later do not accidentally run workflows.
- As a workflow builder user, I want to choose whether a command trigger is case-sensitive, so that I can decide whether
/Ask should match /ask.
- As a workflow builder user, I want to add a mention trigger, so that workflows can run when a message mentions the connected WhatsApp user.
- As a workflow builder user, I want a workflow to support multiple triggers, so that one workflow can handle related commands like
/task and /done.
- As a workflow builder user, I want the most specific trigger in a workflow to win when multiple triggers match, so that command triggers take priority over mention triggers.
- As a workflow builder user, I want multiple matching workflows to run independently, so that separate workflows can listen to the same command if needed.
- As a workflow builder user, I want to scope a workflow to selected contacts and groups, so that it only reacts in the WhatsApp conversations I choose.
- As a workflow builder user, I want to fetch WhatsApp contacts from WasenderAPI, so that I can choose contact scope from real WhatsApp data.
- As a workflow builder user, I want to fetch WhatsApp groups from WasenderAPI, so that I can choose group scope from real WhatsApp data.
- As a workflow builder user, I want to create local tables, so that workflows can store structured data without a database.
- As a workflow builder user, I want to define local table columns, so that I can model use cases like tasks, CRM leads, or reminders.
- As a workflow builder user, I want table columns to support text, number, boolean, date, datetime, select, and JSON, so that common structured data can be represented.
- As a workflow builder user, I want rows to have internal
id, createdAt, and updatedAt fields, so that workflow actions can reliably reference and update records.
- As a workflow builder user, I want to read rows from a local table, so that workflows can inspect existing data.
- As a workflow builder user, I want to create rows in a local table, so that workflows can store new data from WhatsApp messages.
- As a workflow builder user, I want to update rows in a local table, so that workflows can change task status or other stored values.
- As a workflow builder user, I want to delete rows from a local table, so that workflows can remove obsolete local data.
- As a workflow builder user, I want local table mutations to return patches, so that the client can apply runner results to localStorage.
- As a workflow builder user, I want a single Generate block with selectable output modes, so that I do not need separate AI blocks for text and structured data.
- As a workflow builder user, I want Generate output mode
text, so that AI can draft replies or summaries.
- As a workflow builder user, I want Generate output mode
object, so that AI can extract structured records from WhatsApp text.
- As a workflow builder user, I want Generate output mode
array, so that AI can extract multiple structured records from one message.
- As a workflow builder user, I want Generate output mode
choice, so that AI can classify messages into allowed options.
- As a workflow builder user, I want Generate output mode
json, so that advanced workflows can capture flexible JSON output.
- As a workflow builder user, I want to configure schema or options for structured Generate outputs, so that AI output is validated before downstream blocks use it.
- As a workflow builder user, I want Generate blocks to support AI tools, so that the model can call workflow-defined capabilities while generating.
- As a workflow builder user, I want AI tools to be block chains, so that tool behavior can be composed visually from existing actions.
- As a workflow builder user, I want AI tool chains to read local tables, so that AI can use stored workflow data.
- As a workflow builder user, I want AI tool chains to write local tables, so that AI can update data during generation.
- As a workflow builder user, I want AI tool chains to send messages, so that AI can perform WhatsApp side effects when explicitly modeled as tool actions.
- As a workflow builder user, I want AI tool side effects to inherit the run mode, so that dry-run testing cannot accidentally live-send WhatsApp messages.
- As a workflow builder user, I want a Send Message block, so that workflows can send WhatsApp responses.
- As a workflow builder user, I want Send Message blocks to support quoted replies, so that responses can appear as replies to the triggering message.
- As a workflow builder user, I want Send Message blocks to support new messages, so that workflows can send standalone messages to contacts or groups.
- As a workflow builder user, I want dry-run sends by default, so that I can test workflows safely.
- As a workflow builder user, I want an explicit live mode, so that I can intentionally send real WhatsApp messages through WasenderAPI.
- As a workflow builder user, I want a Condition block, so that workflows can branch based on trigger data, block outputs, or table data.
- As a workflow builder user, I want explicit control-flow edges, so that I can see exactly what happens after each block.
- As a workflow builder user, I want blocks to expose named outputs like success, error, true, and false, so that workflow paths are clear.
- As a workflow builder user, I want errors to stop the current path unless an error edge exists, so that failures are predictable.
- As a workflow builder user, I want block fields to use expressions like
{{trigger.message.text}}, so that blocks can reference trigger data and previous block outputs.
- As a workflow builder user, I want expressions to be read-only path interpolation, so that the POC remains safe and understandable.
- As a workflow builder user, I want every block output to be stored under its block ID, so that downstream blocks can reference prior results.
- As a workflow builder user, I want the Test Chat sidebar to send simulated WhatsApp messages into the runner, so that I can test workflows without configuring webhooks.
- As a workflow builder user, I want the Test Chat to pass the current workflow JSON and table state to the runner, so that tests always use the current unsaved canvas state.
- As a workflow builder user, I want to see run steps and outputs, so that I can debug why a workflow did or did not behave as expected.
- As a workflow builder user, I want to see outbound message intents in the run output, so that I can verify what would be sent before enabling live mode.
- As a workflow builder user, I want to see table patches in the run output, so that I can understand what data changed.
- As a workflow builder user, I want the workflow graph to prevent cycles, so that POC execution cannot loop forever.
- As a workflow builder user, I want the runner to enforce execution limits, so that broken workflows fail safely.
- As a developer, I want workflow execution isolated in a deep runner module, so that graph evaluation can be tested without React Flow.
- As a developer, I want WasenderAPI access isolated behind server-side routes, so that API keys are not exposed to the browser.
- As a developer, I want AI SDK integration isolated behind the Generate block executor, so that output modes and tool execution stay manageable.
- As a developer, I want local table operations implemented as pure functions over JSON state, so that table behavior can be tested independently.
- As a developer, I want workflow JSON to be portable, so that future persistence can store the same representation in a database.
- As a developer, I want the POC to avoid real webhook ingestion, so that no database is needed for durable inbound workflow execution yet.
Implementation Decisions
- The top-level product concept is Workflow. Do not introduce a separate Bot domain concept in the POC.
- A Workflow is an editable and runnable graph that starts from one or more WhatsApp triggers.
- All triggers originate from WhatsApp.
- A Run is a single execution of a workflow caused by one incoming WhatsApp-style message.
- Workflow definitions are represented as Workflow JSON containing triggers, blocks, edges, scope, Generate tool definitions, and local table definitions.
- The editor stores Workflow JSON in localStorage.
- Local table schemas and rows are stored in localStorage.
- The POC runner executes posted Workflow JSON rather than loading workflows from server persistence.
- The runner API accepts workflow JSON, a WhatsApp-style message, local table state, and run mode.
- The runner API returns run steps, block outputs, outbound message intents or send results, and table patches.
- The client applies table patches to localStorage after a successful run.
- The Test Chat sidebar is the primary way to execute workflows in the POC.
- Real Wasender webhook ingestion is deferred.
- Workflow scope uses selected WhatsApp contacts and groups.
- Contacts are fetched through WasenderAPI
GET /api/contacts using server-side credentials.
- Groups are fetched through WasenderAPI
GET /api/groups using server-side credentials.
- Sending WhatsApp messages uses WasenderAPI
POST /api/send-message through server-side credentials.
- Quoted replies are represented by passing
replyTo to WasenderAPI POST /api/send-message.
- Send Message blocks default to dry-run behavior and produce outbound message intents.
- Live sending is explicit and applies to all send-message actions in the run, including send-message actions inside AI tool chains.
- A workflow can have multiple triggers.
- If multiple triggers in the same workflow match one message, trigger specificity decides which trigger starts the run.
- Command triggers are more specific than mention triggers.
- If multiple workflows match the same message, each matching workflow gets its own run.
- Command trigger definitions must start with
/, contain no spaces, and use lowercase characters.
- Command matching checks the first token of the message.
- Command trigger matching can be case-sensitive or case-insensitive per trigger.
- The initial block set is WhatsApp Command Trigger, WhatsApp Mention Trigger, Generate, Send Message, Condition, Read Rows, Create Row, Update Row, and Delete Row.
- The Generate block replaces separate generate-text and generate-object blocks.
- Generate supports AI SDK output modes for text, object, array, choice, and JSON.
- Generate blocks can configure a system prompt, prompt template, output mode, schema/options, tools, and max AI steps.
- Generate output is exposed under stable fields such as text, object, array, choice, or JSON based on configured output mode.
- Generate tools are inline to one Generate block in the POC.
- Each AI tool has a name, description, input schema, and a connected block chain.
- AI tool chains can read local tables, write local tables, send messages, perform other supported action blocks, and return data to the Generate block.
- AI tool chains inherit the parent run mode.
- Control-flow edges are explicit and use named handles such as success, error, true, and false.
- Most action blocks expose success and error outputs.
- Condition blocks expose true and false outputs.
- If an error edge exists, errors can continue along that path; otherwise the current path stops.
- If multiple outgoing edges exist from the same output, the POC runs them in deterministic canvas order.
- Expressions use read-only double-brace path interpolation, such as
{{trigger.message.text}} and {{blocks.generate_1.output.object.title}}.
- Expressions can reference trigger data, block outputs, and local table data.
- Missing required expression paths fail the block.
- Local table columns support text, number, boolean, date, datetime, select, and JSON.
- Local table rows always have internal id, createdAt, and updatedAt fields.
- User-defined local table columns cannot be named id, createdAt, or updatedAt.
- The graph is acyclic in the POC.
- React Flow connection validation should prevent edges that create cycles.
- The server runner also validates that posted workflow JSON is acyclic.
- The runner enforces a max block execution count of 100 per run.
- Generate blocks default to AI SDK
stepCountIs(8) or the current documented equivalent for max steps.
- Nested AI tool-chain depth is capped at 2 in the POC.
- React Flow should be used for the canvas with custom nodes and handles.
- React Flow JSON serialization should be respected, but domain Workflow JSON should remain the portable source of truth for execution.
- n8n best practices to follow include explicit trigger nodes, visible action nodes, visible conditionals, test executions, and inspectable execution output.
Testing Decisions
- Good tests should verify external behavior of the runner and table operations, not React component implementation details.
- The workflow runner should be tested as a deep module with JSON input and JSON output.
- Trigger matching should be tested for command first-token behavior, case sensitivity, mention behavior, specificity, and scope checks.
- Graph validation should be tested for cycle rejection and invalid edge handling.
- Expression interpolation should be tested for valid paths, missing required paths, and non-mutating behavior.
- Local table operations should be tested as pure JSON transformations for read, create, update, delete, metadata fields, and invalid columns.
- Table patch application should be tested so localStorage updates can be trusted.
- Send Message execution should be tested in dry-run mode to verify outbound intents for new messages and quoted replies.
- Live Wasender sends should be isolated behind a small adapter that can be mocked in tests.
- Generate block execution should be tested with a fake AI adapter where possible, so output-mode routing and tool-chain execution can be validated without calling OpenAI.
- AI tool-chain execution should be tested to verify inherited run mode, nested depth limits, and side-effect logging.
- UI testing can be deferred for the first POC, but the Test Chat and canvas state should be structured so they can be covered later with integration tests.
Out of Scope
- Database-backed workflow storage.
- Database-backed local table storage.
- Real Wasender webhook ingestion and production message routing.
- User accounts, permissions, sharing, or multi-user collaboration.
- Workflow publishing separate from local enabled/disabled state.
- Media messages, attachments, voice notes, stickers, contacts, locations, or polls.
- Loops, waits, delays, merges, reusable subworkflows, or scheduled triggers.
- External HTTP request blocks.
- A full n8n-style expression language or arbitrary JavaScript execution.
- Streaming AI responses in the initial POC.
- Production observability, billing, quotas, or rate-limit management.
- Durable run history beyond local UI output.
Further Notes
- The POC should use the current AI SDK documentation from local package docs or ai-sdk.dev before implementation. In particular,
generateText, Output.text, Output.object, Output.array, Output.choice, Output.json, tool, and step limits must be verified against installed versions.
- The POC should use the current React Flow documentation before implementation, especially custom nodes, handles, connection validation, save/restore, and TypeScript types.
- The POC should use the WasenderAPI documentation for contacts, groups, send text, and send quoted message.
- Existing reference-projects/whatsapp-logger demonstrates Wasender webhook payload handling and AI SDK tool usage, but reference-projects must not be committed.
- This PRD intentionally avoids the term Bot. The product language is Workflow.
PRD: WhatsApp Workflow Builder POC
Problem Statement
Users need a way to build WhatsApp-native workflows visually, without writing code, so they can react to WhatsApp messages, use AI, send replies, and store structured local data. The first use case is a WhatsApp-driven task manager where a user can send commands like
/task buy milk, have AI extract structured data, store it in a local table, and receive a confirmation message.The POC must prove the workflow model, execution boundary, AI tool composition, and WhatsApp send/fetch integration without introducing a database yet.
Solution
Build a local-first workflow builder using React Flow. Users create workflows made of WhatsApp triggers, action blocks, AI Generate blocks, table action blocks, and condition blocks. Workflow definitions and local table state are stored in browser localStorage as JSON.
Runs execute through a server route handler. The client posts the current workflow JSON, a simulated WhatsApp message, local table state, and run mode to the runner API. The runner evaluates triggers, executes the graph, calls AI SDK for Generate blocks, runs AI tool block chains when called, applies table mutations as patches, and returns run output for the client to display and persist locally.
The POC includes a sidebar Test Chat that simulates WhatsApp messages against the current workflow. Send Message blocks are dry-run by default and produce outbound message intents. In live mode, send-message blocks call WasenderAPI through server-side route handlers.
User Stories
/task buy milkcan start a workflow./Askshould match/ask./taskand/done.id,createdAt, andupdatedAtfields, so that workflow actions can reliably reference and update records.text, so that AI can draft replies or summaries.object, so that AI can extract structured records from WhatsApp text.array, so that AI can extract multiple structured records from one message.choice, so that AI can classify messages into allowed options.json, so that advanced workflows can capture flexible JSON output.{{trigger.message.text}}, so that blocks can reference trigger data and previous block outputs.Implementation Decisions
GET /api/contactsusing server-side credentials.GET /api/groupsusing server-side credentials.POST /api/send-messagethrough server-side credentials.replyToto WasenderAPIPOST /api/send-message./, contain no spaces, and use lowercase characters.{{trigger.message.text}}and{{blocks.generate_1.output.object.title}}.stepCountIs(8)or the current documented equivalent for max steps.Testing Decisions
Out of Scope
Further Notes
generateText,Output.text,Output.object,Output.array,Output.choice,Output.json,tool, and step limits must be verified against installed versions.