v2.0 = v1 + Safety. Same tools, same BBC API, but with mandatory patterns that prevent the silent failures, accidental deletions, and unvalidated deploys that plagued v1.
Three pillars:
list_* to confirmToolPurposeMutating?builderbot_list_projectsList all projectsNobuilderbot_create_projectCreate projectYesbuilderbot_list_flowsList flows in projectNobuilderbot_create_flowCreate flow with keywordsYesbuilderbot_update_flowUpdate flow configYesbuilderbot_delete_flowDelete flow + all answersYes (DESTRUCTIVE)builderbot_list_answersList answers in flowNobuilderbot_create_answerAdd answer to flowYesbuilderbot_update_answerUpdate answer contentYesbuilderbot_delete_answerDelete single answerYes (DESTRUCTIVE)builderbot_validate_botHealth check before deployNobuilderbot_deployDeploy/status/QR/reboot/deleteYes (some actions)
After ANY create/update/delete, call the corresponding list_* tool to confirm:
text
create_flow(projectId, ...) → list_flows(projectId) → search for the new flow
create_answer(...) → list_answers(projectId, flowId) → search for new answer
update_answer(...) → list_answers(projectId, flowId) → confirm content changed
delete_flow(...) → list_flows(projectId) → confirm flow is gone
If verification fails: STOP, report the discrepancy, diagnose, and propose recovery.
Before delete_flow, delete_answer, or deploy(action='delete'):
validate_bot to check for broken referencesWhen any operation fails or verification shows a discrepancy:
text
projectId: UUID (from list_projects)
name: "Human Readable Name" (Title Case, user's language)
label: "snake_case_slug" (lowercase, no spaces)
keywords: ["keyword1", "keyword2"] or ["EVENTS.WELCOME"]
listenKeywords: false (ALWAYS false unless EVENTS.VOICE_NOTE)
transcribeAudio: true only for EVENTS.VOICE_NOTE
interpretImage: true only for EVENTS.MEDIA
analyzeDocument: true only for EVENTS.DOCUMENT
listenKeywords MUST be false for ALL flows EXCEPT EVENTS.VOICE_NOTEEventUse caseRequired flagsEVENTS.WELCOMECatch-all / fallbacklistenKeywords: falseEVENTS.VOICE_NOTEVoice message handlerlistenKeywords: true, transcribeAudio: trueEVENTS.MEDIAImage handlerinterpretImage: trueEVENTS.DOCUMENTDocument handleranalyzeDocument: trueEVENTS.LOCATIONLocation shared—EVENTS.ACTIONButton/list response—
text
type: "add_text"
message: "Your text here" ← MAX 160 chars for WhatsApp
text
type: "add_chatpdf"
message: "" ← MUST be empty string
CRITICAL: NEVER mix add_chatpdf with add_text in the same flow.
Both fire on every message → broken double-response loop.
A flow with add_chatpdf must contain ONLY the add_chatpdf answer.
After creating, configure via update_answer with assistant field:
text
assistant: {
instructions: "You are a helpful assistant for [business]...",
model: "gpt-5.4-nano" (optional)
}
text
type: "add_image"
message: ""
options: { media: { url: "https://public-url.com/image.jpg" }, gotoFlow: {} }
text
type: "add_http"
message: ""
plugins: {
http: {
url: "https://api.example.com/endpoint",
method: "GET" | "POST",
headers: { "Content-Type": "application/json" }, // optional
body: { key: "value" }, // optional, for POST
rules: [] // REQUIRED — always include, even if empty
}
}
CRITICAL: plugins.http.rules is REQUIRED. Omitting it causes backend rejection.
text
type: "add_mute"
plugins: { mute: { status: true, gapTime: 60 } }
options: { gotoFlow: { flowId: "farewell-flow-uuid" } }
Mutes the INDIVIDUAL contact, not the whole bot. gapTime = minutes until auto-unmute.
text
type: "add_intent"
plugins: {
intent: {
rules: [{
conditionRule: "The user wants to talk to a human",
conditionFlowId: "<target-flow-uuid>",
condition: "",
conditionValue: ""
}]
}
}
options.capture = true → bot waits for user reply, passes it to NEXT answer.
NEVER set capture=true on the LAST answer of a flow — value is silently lost.
options.gotoFlow = { flowId: "<target-flow-uuid>" } → redirect after this answer.
text
User request → What type of business?
├── Needs appointments/citas/availability → Pattern 2 (AI) ★ RECOMMENDED
├── Needs Q&A about products/services → Pattern 2 (AI)
├── Needs order tracking/status → Pattern 2 (AI) + HTTP
├── Simple info (hours, location, menu) → Pattern 1 (keyword-based) OK
├── Lead generation / qualification → Pattern 2 (AI) + capture
└── Human handoff / escalation → Pattern 2 (AI) + mute
DEFAULT: Pattern 2 (AI-powered with add_chatpdf) for 80% of real cases.
Only use pure keyword → text flows for very simple, static information.
add_chatpdf with instructions about services, prices, availabilityadd_mute + human handoffassistant.scrapeUrladd_chatpdf with menu, hours, delivery infoadd_text with Google Maps linkadd_chatpdf with catalog, pricing, shippingadd_http to check order APIadd_muteadd_chatpdf with product catalog, pricingadd_chatpdf with event details, schedule, registrationadd_http to registration APItext
list_projects()
├── Project exists → use its projectId
└── Need new → create_project(name) → list_projects() → VERIFY
Before creating anything, plan all flows and share the plan with user:
text
📋 Bot Plan: [Business Name]
├── welcome (EVENTS.WELCOME) — AI assistant with business context
├── escalation (keyword: "agente", "humano") — Human handoff
├── [additional flows based on business needs]
└── Total: N flows
For EACH flow:
text
1. create_flow(projectId, name, label, keywords, ...)
2. list_flows(projectId) → VERIFY flow exists, get flow_id
3. create_answer(projectId, flowId, type, message, ...)
4. list_answers(projectId, flowId) → VERIFY answer exists
5. [If add_chatpdf] update_answer(answerId, assistant: { instructions: "..." })
6. list_answers(projectId, flowId) → VERIFY instructions set
NEVER create all flows first then add answers. Create flow → add answers → verify → next flow.
text
validate_bot(projectId)
├── criticalCount = 0 → Ready to deploy
└── criticalCount > 0 → Fix issues first
├── Dead ends → add answers to empty flows
├── Long messages → shorten to ≤160 chars
├── Dangling capture → remove capture from last answer
└── Missing WELCOME → create welcome flow
text
1. validate_bot(projectId) → MUST pass (criticalCount = 0)
2. Show deployment summary to user:
┌─────────────────────────────┐
│ 📤 DEPLOY SUMMARY │
│ Project: [name] │
│ Flows: N │
│ Answers: M │
│ Validation: ✅ PASS │
│ Confirm? [yes/no] │
└─────────────────────────────┘
3. Wait for user confirmation
4. deploy(projectId, action: "create")
5. deploy(projectId, action: "status") → check status
6. If READY_TO_SCAN → deploy(projectId, action: "qr") → show QR
ErrorDiagnosisRecoveryFlow created but not in listSilent failure / API lagRetry create, verify again"Limit reached"50/50 flows usedList flows, find unused, offer to deleteDuplicate keywordKeyword conflictlist_flows to find conflict, renameAnswer > 160 charsWhatsApp truncationShorten message, verifyDeploy failsValidation issuesRun validate_bot, fix all criticalsadd_chatpdf + add_text in same flowDouble-response bugDelete the add_text answercapture=true on last answerDangling captureRemove capture or add follow-up answer
When creating multiple flows, ALWAYS do them sequentially:
text
FOR EACH flow in plan:
1. create_flow → VERIFY
2. create_answer(s) → VERIFY each
3. [configure AI if needed] → VERIFY
THEN next flow
NEVER batch-create all flows then batch-add answers. This hides partial failures.
After completing bot creation, always provide:
text
📊 BOT REPORT: [Project Name]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Project ID: [uuid]
✅ Flows Created:
1. [flow_name] (id: [uuid]) — [N] answers — [type summary]
2. ...
⚠️ Issues Fixed:
• [description of any issues encountered and resolved]
🔍 Validation: [PASS/FAIL]
• Criticals: 0
• Warnings: [N]
📤 Deploy Status: [DEPLOYED / NOT YET / READY_TO_SCAN]
🔗 Tool Call Trace:
[list of all tool calls made in order]
list_*)validate_bot firstadd_chatpdf and add_text in the same flowlistenKeywords: true on non-VOICE_NOTE flowscapture: true on the last answer of a flowadd_http answers without plugins.http.rules: []For vertical-specific templates and advanced patterns, read:
references/verticals.md — Detailed bot templates per business typereferences/advanced-patterns.md — HTTP integrations, multi-flow routing, knowledge base setup