- Overview
- Architecture
- Features
- Installation
- Configuration
- Usage
- API Reference
- Data Models
- Scheduling Prompts
- Receipt Tracking
- Storage
- Environment Variables
- Custom Flows
- Development
- License
PromptPipe is a Go-based messaging service that delivers adaptive-intervention prompts over WhatsApp using the whatsmeow library. It provides a RESTful API for scheduling messages, sending dynamic or GenAI-generated content, and tracking delivery/read receipts. It also includes a stateful conversation flow (3‑bot architecture: Coordinator, Intake, Feedback). The service is highly configurable via environment variables and supports SQLite and PostgreSQL for the application database.
- Go Core: High-performance, concurrent backend written in Go.
- Whatsmeow Integration: Programmable WhatsApp client for messaging.
- API Layer: RESTful endpoints for scheduling, sending, and tracking prompts (
internal/api
). - Timer & Scheduler Tool: Scheduling for recurring or one-time prompts via a timer and scheduler tool (
internal/flow/timer.go
,internal/flow/scheduler_tool.go
). - Store: Persists receipts, responses, conversation state, schedules, and hooks (
internal/store
). - Message Flow: Pluggable flow generators produce message bodies (
internal/flow
).- Flow Generators:
static
,branch
,genai
, and the statefulconversation
flow; you can register custom generators.
- Flow Generators:
- WhatsApp Client: Handles WhatsApp network communication (
internal/whatsapp
). - GenAI: Optional OpenAI integration for dynamic content (
internal/genai
). - Models: Shared data structures (
internal/models
).
- Schedule prompts at specific times or intervals (cron syntax).
- Messaging abstraction: WhatsApp supported today via whatsmeow; interface allows adding other providers later.
- Send dynamic payloads: text, media, and template messages with custom variables.
- GenAI-enhanced content: Use OpenAI to generate message content dynamically.
- Structured reasoning: Agent modules always request JSON
{thinking, content}
; thinking is surfaced only in debug mode for developers (no toggle to avoid schema drift). - Branch flows: Present selectable branch options to participants.
- Custom flows: Plug in your own
Generator
implementations for fully customized message-generation logic. - Receipt tracking: Capture sent, delivered, and read events.
- Modular design: Integrates with any adaptive-intervention framework.
- Clear REST API: Easy integration with your application.
- Customizable Message Flows: Define and register custom flow generators for handling different prompt types.
- Stateful conversation: 3‑bot architecture with enrollment endpoints and recovery.
# Clone the repository
git clone https://github.com/BTreeMap/PromptPipe.git
cd PromptPipe
# Build the binary
make build
# Or use Go directly:
go build -o build/promptpipe cmd/PromptPipe/main.go
PromptPipe uses two separate databases to clearly separate concerns:
- WhatsApp Database: Used by the whatsmeow library for WhatsApp session data (we don't control this schema)
- Application Database: Used for receipts, responses, and flow state (controlled by PromptPipe)
Create a .env
file or export the following environment variables:
# WhatsApp/Whatsmeow Database Configuration
WHATSAPP_DB_DSN="file:/var/lib/promptpipe/whatsmeow.db?_foreign_keys=on"
# Application Database Configuration
DATABASE_DSN="postgres://user:pass@host:port/dbname?sslmode=disable"
# Legacy Support (DATABASE_URL will be used for application database if DATABASE_DSN is not set)
DATABASE_URL="postgres://user:pass@host:port/dbname?sslmode=disable"
# Other Configuration
PROMPTPIPE_STATE_DIR="/var/lib/promptpipe" # Directory for file-based storage
DEFAULT_SCHEDULE="0 9 * * *" # Default cron schedule (9 AM daily)
API_ADDR=":8080" # API server address
OPENAI_API_KEY="your_openai_api_key" # OpenAI API key for GenAI operations
# Optional GenAI settings
GENAI_MODEL="gpt-4o-mini"
GENAI_TEMPERATURE="0.1"
# Conversation Flow prompts and limits
INTAKE_BOT_PROMPT_FILE="prompts/intake_bot_system.txt"
PROMPT_GENERATOR_PROMPT_FILE="prompts/prompt_generator_system.txt"
FEEDBACK_TRACKER_PROMPT_FILE="prompts/feedback_tracker_system.txt"
CHAT_HISTORY_LIMIT="-1" # -1=unlimited, 0=none, N=last N messages to tools
FEEDBACK_INITIAL_TIMEOUT="15m"
FEEDBACK_FOLLOWUP_DELAY="3h"
SCHEDULER_PREP_TIME_MINUTES="10"
AUTO_FEEDBACK_AFTER_PROMPT_ENABLED="true"
PROMPTPIPE_DEBUG="false"
If no database configuration is provided, both databases will use SQLite files:
- WhatsApp database:
{STATE_DIR}/whatsmeow.db
(with foreign keys enabled) - Application database:
{STATE_DIR}/state.db
WHATSAPP_DB_DSN="postgres://user:pass@host:port/whatsapp_db?sslmode=disable"
DATABASE_DSN="postgres://user:pass@host:port/app_db?sslmode=disable"
DATABASE_DSN="postgres://user:pass@host:port/app_db?sslmode=disable"
# WHATSAPP_DB_DSN not set - will default to SQLite with foreign keys
WHATSAPP_DB_DSN="postgres://user:pass@host:port/whatsapp_db?sslmode=disable"
# DATABASE_DSN not set - will default to SQLite
Important: The whatsmeow library strongly recommends enabling foreign keys for SQLite databases to ensure data integrity. PromptPipe will automatically enable foreign keys in default SQLite configurations for the WhatsApp database.
If you provide a custom SQLite DSN for the WhatsApp database without foreign keys enabled, PromptPipe will log a warning message recommending you add ?_foreign_keys=on
to your connection string.
Example SQLite DSN with foreign keys: file:/path/to/database.db?_foreign_keys=on
# Start the service (reads .env automatically)
./build/promptpipe [flags]
-api-addr
API server address (overrides $API_ADDR)-qr-output
path to write login QR code-numeric-code
use numeric login code instead of QR code-state-dir
state directory for PromptPipe data (overrides $PROMPTPIPE_STATE_DIR)-whatsapp-db-dsn
WhatsApp/whatsmeow DB connection string (overrides $WHATSAPP_DB_DSN)-app-db-dsn
application DB connection string (overrides $DATABASE_DSN or $DATABASE_URL)-openai-api-key
OpenAI API key (overrides $OPENAI_API_KEY)-default-cron
default cron schedule for prompts (overrides $DEFAULT_SCHEDULE)-intake-bot-prompt-file
path to intake bot system prompt file-prompt-generator-prompt-file
path to prompt generator system prompt file-feedback-tracker-prompt-file
path to feedback tracker system prompt file-chat-history-limit
limit of history messages exposed to tools (-1=no limit)-feedback-initial-timeout
e.g., 15m-feedback-followup-delay
e.g., 3h-genai-temperature
0.0–1.0-genai-model
OpenAI model name-scheduler-prep-time-minutes
default 10-auto-feedback-after-prompt-enabled
default true-debug
enable debug logging + structured thinking surfacing
All API endpoints expect and return JSON.
Schedules a new prompt to be sent according to a cron expression.
Request Body: Prompt
object (see Data Models). Supports optional system_prompt
and user_prompt
for GenAI. Provide a schedule
object to define timing.
Response Body: {"status":"ok"}
Responses:
201 Created
: Prompt successfully scheduled.400 Bad Request
: Invalid request payload.500 Internal Server Error
: Error scheduling the prompt.
Sends a prompt immediately.
Request Body: Prompt
object (see Data Models; any schedule
is ignored). Supports optional system_prompt
and user_prompt
to generate dynamic content.
Response Body: {"status":"ok"}
Responses:
200 OK
: Prompt successfully sent.400 Bad Request
: Invalid request payload.500 Internal Server Error
: Error sending the prompt.
Fetches all stored delivery and read receipt events.
Response Body: Array of Receipt
objects (see Data Models)
Responses:
200 OK
: Successfully retrieved receipts.500 Internal Server Error
: Error fetching receipts.
Collects a participant's response message.
Request Body: Response
object (see Data Models).
Response Body: {"status":"ok"}
Responses:
201 Created
: Response successfully recorded.400 Bad Request
: Invalid request payload.500 Internal Server Error
: Error recording response.
Retrieves all collected participant responses.
Response Body: Array of Response
objects.
Responses:
200 OK
: Successfully retrieved responses.500 Internal Server Error
: Error fetching responses.
Provides statistics over collected responses (total count, per sender counts, average response length).
Response Body: JSON object with fields:
total_responses
: integerresponses_per_sender
: map of sender to countavg_response_length
: float
Responses:
200 OK
: Successfully retrieved statistics.500 Internal Server Error
: Error computing statistics.
Basic health/status endpoint.
Responses:
200 OK
healthy503 Service Unavailable
degraded, with error info
GET /timers
– list active timersGET /timers/{id}
– get info for a timerDELETE /timers/{id}
– cancel a timer
POST /conversation/participants
– enroll a participantGET /conversation/participants
– list participantsGET /conversation/participants/{id}
– get participantPUT /conversation/participants/{id}
– update participantDELETE /conversation/participants/{id}
– remove participant
Represents a message to be sent, supporting multiple flow types (static, genai, branch, conversation, or custom).
{
"to": "+15551234567",
"schedule": {
"minute": 0,
"hour": 9,
"weekday": 1,
"timezone": "America/Toronto"
},
"type": "static | genai | branch | conversation | custom",
"state": "string (custom flows only)",
"body": "string (for static)",
"system_prompt": "string (for genai/conversation)",
"user_prompt": "string (for genai/conversation)",
"branch_options": [ { "label": "string", "body": "string" } ]
}
to
: The recipient's WhatsApp phone number in E.164 format (e.g.,+15551234567
).schedule
: Object for minute/hour/day/month/weekday + optional timezone. Required for/schedule
if no default schedule is configured.type
: The type of flow to use for generating the message (e.g., "static", "genai", "branch", "custom").state
: Optional current state for custom flows.body
: The text content of the message or prompt template.system_prompt
: Optional system prompt for generating dynamic content using GenAI.user_prompt
: Optional user prompt for generating dynamic content using GenAI.branch_options
: Optional list of branch options for "branch" type flows.
Represents a delivery or read receipt for a sent message.
{
"to": "string (E.164 phone number)",
"status": "string (e.g., \"sent\", \"delivered\", \"read\", \"failed\", \"error\", \"scheduled\", \"cancelled\")",
"time": "int64 (Unix timestamp)"
}
to
: The recipient's WhatsApp phone number.status
: The status of the message (e.g., "sent", "delivered", "read", "failed", "error", "scheduled", "cancelled").time
: Unix timestamp of when the receipt event occurred.
Represents an incoming response from a participant.
{
"from": "string (E.164 phone number)",
"body": "string (message content)",
"time": "int64 (Unix timestamp)"
}
from
: The sender's WhatsApp phone number in E.164 format.body
: The text content of the response message.time
: Unix timestamp of when the response was received.
The /schedule
endpoint allows you to define messages that will be sent out based on a structured schedule object (minute/hour/day/month/weekday, optional timezone). A simple cron-like conversion is supported internally for timers.
The system tracks message events (sent, delivered, read) and stores them. These can be retrieved via the /receipts
endpoint. The internal/store
package handles the persistence of these receipts.
PromptPipe uses two separate databases to separate concerns:
- WhatsApp database (managed by whatsmeow) – configured via
WHATSAPP_DB_DSN
or-whatsapp-db-dsn
(SQLite by default at{STATE_DIR}/whatsmeow.db?_foreign_keys=on
). - Application database (managed by PromptPipe) – configured via
DATABASE_DSN
or-app-db-dsn
(SQLite by default at{STATE_DIR}/state.db?_foreign_keys=on
).
PostgreSQL is supported for the application database by providing a postgres://...
DSN. The project auto-detects DSN type. An in-memory store exists primarily for tests.
PromptPipe uses a state directory to store SQLite databases and other persistent data:
- Default:
/var/lib/promptpipe
- Environment Variable:
PROMPTPIPE_STATE_DIR
- Command Line Flag:
-state-dir
The SQLite database file is automatically placed at {state-dir}/promptpipe.db
unless a specific database DSN is provided.
The database can be configured in several ways (in order of precedence):
- Command Line Flags:
-whatsapp-db-dsn
,-app-db-dsn
- Environment Variables:
WHATSAPP_DB_DSN
,DATABASE_DSN
(or legacyDATABASE_URL
) - Default: SQLite databases under
{state-dir}
with foreign keys enabled
Examples:
# Use SQLite in custom location
./build/promptpipe -app-db-dsn "file:/path/to/state.db?_foreign_keys=on"
# Use PostgreSQL
./build/promptpipe -app-db-dsn "postgres://user:password@localhost/promptpipe?sslmode=disable"
# Use custom state directory (SQLite will be at /custom/path/promptpipe.db)
./build/promptpipe -state-dir /custom/path
# Environment variable configuration
export DATABASE_DSN="postgres://user:password@localhost/promptpipe?sslmode=disable"
export PROMPTPIPE_STATE_DIR="/custom/state/dir"
./build/promptpipe
Variable | Description |
---|---|
PROMPTPIPE_STATE_DIR | State directory for PromptPipe data |
WHATSAPP_DB_DSN | WhatsApp/whatsmeow DB DSN |
DATABASE_DSN | Application DB DSN (or legacy DATABASE_URL) |
DEFAULT_SCHEDULE | Default cron schedule for /schedule when none provided |
API_ADDR | API server address |
OPENAI_API_KEY | API key for OpenAI GenAI operations |
GENAI_MODEL | OpenAI model (default gpt-4o-mini) |
GENAI_TEMPERATURE | OpenAI temperature (default 0.1) |
INTAKE_BOT_PROMPT_FILE | Path to intake system prompt |
PROMPT_GENERATOR_PROMPT_FILE | Path to prompt-generator system prompt |
FEEDBACK_TRACKER_PROMPT_FILE | Path to feedback tracker system prompt |
CHAT_HISTORY_LIMIT | History window exposed to tools (-1/0/N) |
FEEDBACK_INITIAL_TIMEOUT | Initial feedback timeout (e.g., 15m) |
FEEDBACK_FOLLOWUP_DELAY | Follow-up feedback delay (e.g., 3h) |
SCHEDULER_PREP_TIME_MINUTES | Minutes before target time for prep notifications |
AUTO_FEEDBACK_AFTER_PROMPT_ENABLED | Auto-enforce feedback session after scheduled prompt inactivity |
PROMPTPIPE_DEBUG | Enable debug mode and write API call logs under {STATE_DIR}/debug |
You can define your own message-generation flows by implementing the flow.Generator
interface:
type Generator interface {
Generate(ctx context.Context, p models.Prompt) (string, error)
}
Then register your generator with a PromptType
in an init()
function:
func init() {
flow.Register(models.PromptTypeCustom, &MyCustomGenerator{})
}
Set type: "custom"
in your Prompt
JSON; the API will dispatch to your generator.
- Code is organized in the
internal/
directory by module (API, scheduler, store, WhatsApp integration, GenAI, models). - Tests are provided for each module.
- To run tests:
go test ./...
This project is licensed under the MIT License. See the LICENSE file for details.