Skip to main content
Build structured WhatsApp workflows with AI-powered decisions and dynamic content generation. Flows inherit the WhatsApp configuration from the running conversation. Only set whatsapp_config_id on a node when you explicitly need to override that default.

Quickstart

from kapso.builder.flows import Flow
from kapso.builder.flows.nodes import StartNode, SendTextNode, WaitForResponseNode, DecideNode, AgentNode
from kapso.builder.flows.nodes.agent import FlowAgentWebhook
from kapso.builder.flows.nodes.decide import Condition
from kapso.builder.ai.field import AIField

# Create flow
flow = Flow(name="customer_support")

# Add nodes
start = StartNode(id="start_1234")
greeting = SendTextNode(
    id="greeting_5678",
    message=AIField("Greet the customer and ask how you can help"),
    provider_model_name="claude-sonnet-4-20250514"
)
wait = WaitForResponseNode(
    id="wait_9012"
)
decide = DecideNode(
    id="decide_3456",
    provider_model_name="claude-sonnet-4-20250514",
    conditions=[
        Condition(label="order_help", description="Customer needs help with orders, shipping, returns"),
        Condition(label="tech_support", description="Technical issues, account problems, app troubleshooting")
    ]
)

# Order specialist agent with e-commerce tools
order_expert = AgentNode(
    id="order_expert_7890",
    system_prompt="You are an order specialist. Help customers with orders, shipping, returns, and payment issues. Use tools to check order status and process changes.",
    provider_model_name="claude-sonnet-4-20250514",
    temperature=0.1,
    max_iterations=10,
    webhooks=[
        FlowAgentWebhook(
            name="check_order",
            url="https://api.store.com/orders/{{order_id}}",
            http_method="GET",
            description="Get order status and details by order ID"
        ),
        FlowAgentWebhook(
            name="update_shipping",
            url="https://api.store.com/orders/{{order_id}}/shipping",
            http_method="PUT",
            description="Update shipping address or method"
        )
    ]
)

# Technical support agent with diagnostic tools
tech_expert = AgentNode(
    id="tech_expert_1234",
    system_prompt="You are a technical support specialist. Help customers with app issues, account problems, and technical troubleshooting. Use tools to check account status and system diagnostics.",
    provider_model_name="claude-sonnet-4-20250514", 
    temperature=0.1,
    max_iterations=8,
    webhooks=[
        FlowAgentWebhook(
            name="check_account",
            url="https://api.store.com/users/{{phone_number}}/account",
            http_method="GET",
            description="Get customer account status and details"
        ),
        FlowAgentWebhook(
            name="system_status",
            url="https://api.store.com/system/status",
            http_method="GET",
            description="Check system status and known issues"
        )
    ]
)

flow.add_node(start)
flow.add_node(greeting)
flow.add_node(wait)
flow.add_node(decide)
flow.add_node(order_expert)
flow.add_node(tech_expert)

# Connect nodes
flow.add_edge("start_1234", "greeting_5678")
flow.add_edge("greeting_5678", "wait_9012")
flow.add_edge("wait_9012", "decide_3456")
flow.add_edge("decide_3456", "order_expert_7890", label="order_help")
flow.add_edge("decide_3456", "tech_expert_1234", label="tech_support")

flow.validate()
For POST webhooks, supply a body_schema so the agent’s LLM fills the payload correctly. The body field should only be used for static JSON or when you already have the exact values available in variables. In most cases you define one or the other—avoid providing both unless you intentionally need to combine a constant with schema-generated content.
Deploy with kapso flow push customer_support

Flows vs Agents

FlowsAgents
Fixed graph structureDynamic reasoning
AI for decisions & contentLLM controls entire flow
WhatsApp-specific nodesGeneral conversation
Semi-deterministicNon-deterministic
Structured workflowsOpen-ended dialogue
I