BotsWorkflowsWorkflow Recipes

Workflow Recipes

Six complete workflow examples you can use as starting points. Each includes the full JSON graph, a flow diagram, and a walkthrough of how it works.


Recipe 1: Customer Support with Escalation

A support bot that classifies intent, looks up relevant data, and escalates to a human when necessary.

Nodes: trigger, llm_call, logic (switch), mcp_tool, action (human_handoff), send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "On Message" },
      "position": { "x": 0, "y": 200 }
    },
    {
      "id": "classify",
      "type": "llm_call",
      "data": {
        "prompt": "Classify this customer message into exactly one category: billing, refund, other.\n\nMessage: {{message}}\n\nRespond with only the category name, nothing else.",
        "outputVariable": "intent",
        "label": "Classify Intent"
      },
      "position": { "x": 250, "y": 200 }
    },
    {
      "id": "route",
      "type": "logic",
      "data": {
        "logicType": "switch",
        "variable": "intent.text",
        "cases": ["billing", "refund"],
        "label": "Route by Intent"
      },
      "position": { "x": 500, "y": 200 }
    },
    {
      "id": "lookup_account",
      "type": "mcp_tool",
      "data": {
        "toolName": "database__query",
        "arguments": "{\"sql\": \"SELECT * FROM accounts WHERE user_id = '{{user_id}}' LIMIT 1\"}",
        "outputVariable": "account",
        "label": "Look Up Account"
      },
      "position": { "x": 750, "y": 50 }
    },
    {
      "id": "billing_response",
      "type": "llm_call",
      "data": {
        "prompt": "The customer asked about billing. Their account info: {{account}}. Their message: {{message}}. Provide a helpful response.",
        "outputVariable": "ai_response",
        "label": "Generate Billing Response"
      },
      "position": { "x": 1000, "y": 50 }
    },
    {
      "id": "send_billing",
      "type": "send_message",
      "data": { "message": "{{ai_response.text}}", "label": "Send Billing Info" },
      "position": { "x": 1250, "y": 50 }
    },
    {
      "id": "check_order",
      "type": "mcp_tool",
      "data": {
        "toolName": "orders__get_latest_order",
        "arguments": "{\"user_id\": \"{{user_id}}\"}",
        "outputVariable": "order",
        "label": "Check Order"
      },
      "position": { "x": 750, "y": 250 }
    },
    {
      "id": "check_amount",
      "type": "logic",
      "data": {
        "logicType": "if_else",
        "condition": "{{order.total}} > 100",
        "label": "Amount > $100?"
      },
      "position": { "x": 1000, "y": 250 }
    },
    {
      "id": "escalate",
      "type": "action",
      "data": {
        "actionType": "human_handoff",
        "reason": "Refund request over $100 for order {{order.id}}",
        "label": "Escalate"
      },
      "position": { "x": 1250, "y": 200 }
    },
    {
      "id": "process_refund",
      "type": "mcp_tool",
      "data": {
        "toolName": "stripe__create_refund",
        "arguments": "{\"payment_id\": \"{{order.payment_id}}\"}",
        "outputVariable": "refund",
        "label": "Process Refund"
      },
      "position": { "x": 1250, "y": 350 }
    },
    {
      "id": "send_refund_confirm",
      "type": "send_message",
      "data": {
        "message": "Your refund for order #{{order.id}} has been processed. You should see the credit within 5-10 business days.",
        "label": "Confirm Refund"
      },
      "position": { "x": 1500, "y": 350 }
    },
    {
      "id": "general_response",
      "type": "llm_call",
      "data": {
        "prompt": "The customer sent: {{message}}. Provide a helpful general response.",
        "outputVariable": "ai_response",
        "label": "General Response"
      },
      "position": { "x": 750, "y": 450 }
    },
    {
      "id": "send_general",
      "type": "send_message",
      "data": { "message": "{{ai_response.text}}", "label": "Send Response" },
      "position": { "x": 1000, "y": 450 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "classify" },
    { "id": "e2", "source": "classify", "target": "route" },
    { "id": "e3", "source": "route", "target": "lookup_account", "sourceHandle": "case_billing" },
    { "id": "e4", "source": "lookup_account", "target": "billing_response" },
    { "id": "e5", "source": "billing_response", "target": "send_billing" },
    { "id": "e6", "source": "route", "target": "check_order", "sourceHandle": "case_refund" },
    { "id": "e7", "source": "check_order", "target": "check_amount" },
    { "id": "e8", "source": "check_amount", "target": "escalate", "sourceHandle": "true" },
    { "id": "e9", "source": "check_amount", "target": "process_refund", "sourceHandle": "false" },
    { "id": "e10", "source": "process_refund", "target": "send_refund_confirm" },
    { "id": "e11", "source": "route", "target": "general_response", "sourceHandle": "case_default" },
    { "id": "e12", "source": "general_response", "target": "send_general" }
  ]
}

Recipe 2: Order Status with ID Extraction

A focused workflow that extracts an order number from the message, looks it up, and responds with the status.

Nodes: trigger, llm_call, logic (if_else), mcp_tool, send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "On Message" },
      "position": { "x": 0, "y": 100 }
    },
    {
      "id": "extract",
      "type": "llm_call",
      "data": {
        "prompt": "Extract the order number from this message. If no order number is found, respond with 'NONE'.\n\nMessage: {{message}}\n\nRespond with only the order number or 'NONE'.",
        "outputVariable": "order_id_result",
        "label": "Extract Order ID"
      },
      "position": { "x": 250, "y": 100 }
    },
    {
      "id": "has_order_id",
      "type": "logic",
      "data": {
        "logicType": "if_else",
        "condition": "{{order_id_result.text}} != NONE",
        "label": "Order ID Found?"
      },
      "position": { "x": 500, "y": 100 }
    },
    {
      "id": "lookup",
      "type": "mcp_tool",
      "data": {
        "toolName": "orders__get_order",
        "arguments": "{\"order_id\": \"{{order_id_result.text}}\"}",
        "outputVariable": "order",
        "label": "Look Up Order"
      },
      "position": { "x": 750, "y": 50 }
    },
    {
      "id": "format_response",
      "type": "llm_call",
      "data": {
        "prompt": "Format this order data into a friendly status message:\n\n{{order}}\n\nInclude: order ID, current status, tracking info, and estimated delivery if available.",
        "outputVariable": "status_msg",
        "label": "Format Status"
      },
      "position": { "x": 1000, "y": 50 }
    },
    {
      "id": "send_status",
      "type": "send_message",
      "data": { "message": "{{status_msg.text}}", "label": "Send Status" },
      "position": { "x": 1250, "y": 50 }
    },
    {
      "id": "ask_order",
      "type": "send_message",
      "data": {
        "message": "I can help you check your order status. Please share your order number (e.g., #12345) and I will look it up for you.",
        "label": "Ask for Order Number"
      },
      "position": { "x": 750, "y": 200 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "extract" },
    { "id": "e2", "source": "extract", "target": "has_order_id" },
    { "id": "e3", "source": "has_order_id", "target": "lookup", "sourceHandle": "true" },
    { "id": "e4", "source": "lookup", "target": "format_response" },
    { "id": "e5", "source": "format_response", "target": "send_status" },
    { "id": "e6", "source": "has_order_id", "target": "ask_order", "sourceHandle": "false" }
  ]
}

Recipe 3: Multi-Language FAQ

A bot that detects the user’s language, searches a knowledge base, and responds in the user’s language.

Nodes: trigger, llm_call, logic (if_else), mcp_tool, send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "On Message" },
      "position": { "x": 0, "y": 150 }
    },
    {
      "id": "detect_lang",
      "type": "llm_call",
      "data": {
        "prompt": "Detect the language of this text. Respond with only the ISO 639-1 code (e.g., en, es, fr, de, ja, zh).\n\nText: {{message}}",
        "outputVariable": "lang",
        "label": "Detect Language"
      },
      "position": { "x": 250, "y": 150 }
    },
    {
      "id": "is_english",
      "type": "logic",
      "data": {
        "logicType": "if_else",
        "condition": "{{lang.text}} == en",
        "label": "Is English?"
      },
      "position": { "x": 500, "y": 150 }
    },
    {
      "id": "search_faq_en",
      "type": "mcp_tool",
      "data": {
        "toolName": "knowledge__search",
        "arguments": "{\"query\": \"{{message}}\", \"limit\": 3}",
        "outputVariable": "faq_results",
        "label": "Search FAQ"
      },
      "position": { "x": 750, "y": 50 }
    },
    {
      "id": "answer_en",
      "type": "llm_call",
      "data": {
        "prompt": "Answer this question using the FAQ results below. If the FAQ does not cover it, say so.\n\nQuestion: {{message}}\n\nFAQ Results:\n{{faq_results}}",
        "outputVariable": "answer",
        "label": "Generate Answer"
      },
      "position": { "x": 1000, "y": 50 }
    },
    {
      "id": "send_en",
      "type": "send_message",
      "data": { "message": "{{answer.text}}", "label": "Send (English)" },
      "position": { "x": 1250, "y": 50 }
    },
    {
      "id": "translate_to_en",
      "type": "llm_call",
      "data": {
        "prompt": "Translate the following text to English. Respond with only the translation.\n\nText: {{message}}",
        "outputVariable": "translated_query",
        "label": "Translate to English"
      },
      "position": { "x": 750, "y": 300 }
    },
    {
      "id": "search_faq_translated",
      "type": "mcp_tool",
      "data": {
        "toolName": "knowledge__search",
        "arguments": "{\"query\": \"{{translated_query.text}}\", \"limit\": 3}",
        "outputVariable": "faq_results",
        "label": "Search FAQ"
      },
      "position": { "x": 1000, "y": 300 }
    },
    {
      "id": "answer_translated",
      "type": "llm_call",
      "data": {
        "prompt": "Answer this question using the FAQ results below.\n\nQuestion: {{translated_query.text}}\n\nFAQ Results:\n{{faq_results}}",
        "outputVariable": "answer_en",
        "label": "Answer (EN)"
      },
      "position": { "x": 1250, "y": 300 }
    },
    {
      "id": "translate_back",
      "type": "llm_call",
      "data": {
        "prompt": "Translate the following text to the language with ISO code '{{lang.text}}'. Respond with only the translation.\n\nText: {{answer_en.text}}",
        "outputVariable": "final_answer",
        "label": "Translate Back"
      },
      "position": { "x": 1500, "y": 300 }
    },
    {
      "id": "send_translated",
      "type": "send_message",
      "data": { "message": "{{final_answer.text}}", "label": "Send (Translated)" },
      "position": { "x": 1750, "y": 300 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "detect_lang" },
    { "id": "e2", "source": "detect_lang", "target": "is_english" },
    { "id": "e3", "source": "is_english", "target": "search_faq_en", "sourceHandle": "true" },
    { "id": "e4", "source": "search_faq_en", "target": "answer_en" },
    { "id": "e5", "source": "answer_en", "target": "send_en" },
    { "id": "e6", "source": "is_english", "target": "translate_to_en", "sourceHandle": "false" },
    { "id": "e7", "source": "translate_to_en", "target": "search_faq_translated" },
    { "id": "e8", "source": "search_faq_translated", "target": "answer_translated" },
    { "id": "e9", "source": "answer_translated", "target": "translate_back" },
    { "id": "e10", "source": "translate_back", "target": "send_translated" }
  ]
}

Recipe 4: Identity-Verified Refund

A refund bot that verifies the customer’s identity via email OTP before accessing their account or processing any refund. Uses auth_gate and human_handoff.

Nodes: trigger, auth_gate, mcp_tool, logic (if_else), llm_call, action (human_handoff), send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "On Message" },
      "position": { "x": 0, "y": 150 }
    },
    {
      "id": "verify",
      "type": "auth_gate",
      "data": {
        "provider": "resend",
        "contact_type": "email",
        "outputVariable": "verified_email",
        "label": "Verify Email"
      },
      "position": { "x": 250, "y": 150 }
    },
    {
      "id": "auth_failed",
      "type": "send_message",
      "data": {
        "message": "We were unable to verify your identity. Please try again or contact support directly.",
        "label": "Auth Failed"
      },
      "position": { "x": 500, "y": 300 }
    },
    {
      "id": "lookup_orders",
      "type": "mcp_tool",
      "data": {
        "toolName": "database__query",
        "arguments": "{\"sql\": \"SELECT * FROM orders WHERE customer_email = '{{verified_email}}' ORDER BY created_at DESC LIMIT 5\"}",
        "outputVariable": "orders",
        "label": "Look Up Orders"
      },
      "position": { "x": 500, "y": 50 }
    },
    {
      "id": "extract_order",
      "type": "llm_call",
      "data": {
        "prompt": "The customer wants a refund. Their message: {{message}}\n\nTheir recent orders:\n{{orders}}\n\nIdentify which order they want to refund. Respond with JSON: {\"order_id\": \"...\", \"amount\": ...}",
        "outputVariable": "refund_target",
        "label": "Identify Order"
      },
      "position": { "x": 750, "y": 50 }
    },
    {
      "id": "parse_amount",
      "type": "code_block",
      "data": {
        "code": "try { const parsed = JSON.parse(ctx.variables.refund_target.text); ctx.variables.refund_order_id = parsed.order_id; ctx.variables.refund_amount = parsed.amount || 0; } catch(e) { ctx.variables.refund_amount = 0; }",
        "label": "Parse Amount"
      },
      "position": { "x": 1000, "y": 50 }
    },
    {
      "id": "check_amount",
      "type": "logic",
      "data": {
        "logicType": "if_else",
        "condition": "{{refund_amount}} > 500",
        "label": "Amount > $500?"
      },
      "position": { "x": 1250, "y": 50 }
    },
    {
      "id": "escalate",
      "type": "action",
      "data": {
        "actionType": "human_handoff",
        "reason": "Refund of ${{refund_amount}} for order {{refund_order_id}} (customer: {{verified_email}})",
        "label": "Finance Approval"
      },
      "position": { "x": 1500, "y": 0 }
    },
    {
      "id": "process_refund",
      "type": "mcp_tool",
      "data": {
        "toolName": "stripe__create_refund",
        "arguments": "{\"order_id\": \"{{refund_order_id}}\"}",
        "outputVariable": "refund_result",
        "label": "Process Refund"
      },
      "position": { "x": 1500, "y": 150 }
    },
    {
      "id": "confirm_refund",
      "type": "send_message",
      "data": {
        "message": "Your refund of ${{refund_amount}} for order #{{refund_order_id}} has been processed. You will see the credit in 5-10 business days.",
        "label": "Confirm Refund"
      },
      "position": { "x": 1750, "y": 150 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "verify" },
    { "id": "e2", "source": "verify", "target": "lookup_orders", "sourceHandle": "verified" },
    { "id": "e3", "source": "verify", "target": "auth_failed", "sourceHandle": "failed" },
    { "id": "e4", "source": "lookup_orders", "target": "extract_order" },
    { "id": "e5", "source": "extract_order", "target": "parse_amount" },
    { "id": "e6", "source": "parse_amount", "target": "check_amount" },
    { "id": "e7", "source": "check_amount", "target": "escalate", "sourceHandle": "true" },
    { "id": "e8", "source": "check_amount", "target": "process_refund", "sourceHandle": "false" },
    { "id": "e9", "source": "process_refund", "target": "confirm_refund" }
  ]
}

Recipe 5: Appointment Scheduler with Reminders

A WhatsApp bot that checks availability, books appointments, and sends automated reminders.

Nodes: trigger, llm_call, mcp_tool, logic, schedule_message, send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "On Message" },
      "position": { "x": 0, "y": 150 }
    },
    {
      "id": "extract_pref",
      "type": "llm_call",
      "data": {
        "prompt": "Extract the appointment date and time preference from this message. Respond with JSON: {\"date\": \"YYYY-MM-DD\", \"time\": \"HH:MM\", \"service\": \"...\"}\nIf no clear preference, use the next available business day at 10:00.\n\nMessage: {{message}}",
        "outputVariable": "preference",
        "label": "Extract Preference"
      },
      "position": { "x": 250, "y": 150 }
    },
    {
      "id": "parse_pref",
      "type": "code_block",
      "data": {
        "code": "try { const p = JSON.parse(ctx.variables.preference.text); ctx.variables.pref_date = p.date; ctx.variables.pref_time = p.time; ctx.variables.pref_service = p.service || 'general'; } catch(e) { ctx.variables.pref_date = ''; ctx.variables.pref_time = '10:00'; }",
        "label": "Parse"
      },
      "position": { "x": 500, "y": 150 }
    },
    {
      "id": "check_avail",
      "type": "mcp_tool",
      "data": {
        "toolName": "calendar__check_availability",
        "arguments": "{\"date\": \"{{pref_date}}\", \"time\": \"{{pref_time}}\"}",
        "outputVariable": "availability",
        "label": "Check Availability"
      },
      "position": { "x": 750, "y": 150 }
    },
    {
      "id": "is_available",
      "type": "logic",
      "data": {
        "logicType": "if_else",
        "condition": "{{availability.available}} == true",
        "label": "Slot Available?"
      },
      "position": { "x": 1000, "y": 150 }
    },
    {
      "id": "book",
      "type": "mcp_tool",
      "data": {
        "toolName": "calendar__create_appointment",
        "arguments": "{\"date\": \"{{pref_date}}\", \"time\": \"{{pref_time}}\", \"service\": \"{{pref_service}}\", \"customer_name\": \"{{user_name}}\", \"customer_id\": \"{{user_id}}\"}",
        "outputVariable": "booking",
        "label": "Book Appointment"
      },
      "position": { "x": 1250, "y": 50 }
    },
    {
      "id": "confirm",
      "type": "send_message",
      "data": {
        "message": "Your appointment is confirmed for {{pref_date}} at {{pref_time}}. Booking reference: {{booking.reference}}. You will receive reminders before your appointment.",
        "label": "Confirm"
      },
      "position": { "x": 1500, "y": 0 }
    },
    {
      "id": "reminder_24h",
      "type": "schedule_message",
      "data": {
        "message": "Reminder: Your appointment is tomorrow at {{pref_time}}. Reply CANCEL to cancel or RESCHEDULE to change.",
        "delay_minutes": 1380,
        "label": "24h Reminder"
      },
      "position": { "x": 1500, "y": 100 }
    },
    {
      "id": "reminder_1h",
      "type": "schedule_message",
      "data": {
        "message": "Your appointment is in 1 hour at {{pref_time}}. See you soon!",
        "delay_minutes": 1440,
        "label": "1h Reminder"
      },
      "position": { "x": 1500, "y": 200 }
    },
    {
      "id": "suggest",
      "type": "llm_call",
      "data": {
        "prompt": "The requested time slot ({{pref_date}} at {{pref_time}}) is not available. Available alternatives: {{availability.alternatives}}. Suggest 2-3 alternatives in a friendly message.",
        "outputVariable": "suggestion",
        "label": "Suggest Alternatives"
      },
      "position": { "x": 1250, "y": 300 }
    },
    {
      "id": "send_suggest",
      "type": "send_message",
      "data": { "message": "{{suggestion.text}}", "label": "Send Alternatives" },
      "position": { "x": 1500, "y": 300 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "extract_pref" },
    { "id": "e2", "source": "extract_pref", "target": "parse_pref" },
    { "id": "e3", "source": "parse_pref", "target": "check_avail" },
    { "id": "e4", "source": "check_avail", "target": "is_available" },
    { "id": "e5", "source": "is_available", "target": "book", "sourceHandle": "true" },
    { "id": "e6", "source": "book", "target": "confirm" },
    { "id": "e7", "source": "book", "target": "reminder_24h" },
    { "id": "e8", "source": "book", "target": "reminder_1h" },
    { "id": "e9", "source": "is_available", "target": "suggest", "sourceHandle": "false" },
    { "id": "e10", "source": "suggest", "target": "send_suggest" }
  ]
}

Recipe 6: Incident Triage with Bot Delegation

A monitoring bot that receives alerts, parses severity, and delegates to specialist bots. Uses delegate_to_bot and send_proactive.

Nodes: trigger, llm_call, code_block, logic (switch), delegate_to_bot, send_proactive, action (human_handoff), send_message

Workflow JSON

{
  "nodes": [
    {
      "id": "trigger",
      "type": "trigger",
      "data": { "triggerType": "message_received", "label": "Alert Received" },
      "position": { "x": 0, "y": 200 }
    },
    {
      "id": "parse",
      "type": "llm_call",
      "data": {
        "prompt": "Parse this monitoring alert. Extract severity (P1, P2, or P3), affected service, and a one-line summary.\n\nAlert: {{message}}\n\nRespond with JSON: {\"severity\": \"P1|P2|P3\", \"service\": \"...\", \"summary\": \"...\"}",
        "outputVariable": "alert_parsed",
        "label": "Parse Alert"
      },
      "position": { "x": 250, "y": 200 }
    },
    {
      "id": "extract",
      "type": "code_block",
      "data": {
        "code": "try { const a = JSON.parse(ctx.variables.alert_parsed.text); ctx.variables.severity = a.severity || 'P3'; ctx.variables.service = a.service || 'unknown'; ctx.variables.summary = a.summary || ctx.variables.message; } catch(e) { ctx.variables.severity = 'P3'; ctx.variables.summary = ctx.variables.message; }",
        "label": "Extract Fields"
      },
      "position": { "x": 500, "y": 200 }
    },
    {
      "id": "route",
      "type": "logic",
      "data": {
        "logicType": "switch",
        "variable": "severity",
        "cases": ["P1", "P2"],
        "label": "Route by Severity"
      },
      "position": { "x": 750, "y": 200 }
    },
    {
      "id": "delegate_p1",
      "type": "delegate_to_bot",
      "data": {
        "target_bot_id": "bt_oncall_bot",
        "outputVariable": "oncall_response",
        "context": "{\"severity\": \"P1\", \"service\": \"{{service}}\", \"summary\": \"{{summary}}\"}",
        "label": "Oncall Bot (P1)"
      },
      "position": { "x": 1000, "y": 50 }
    },
    {
      "id": "notify_ops_p1",
      "type": "send_proactive",
      "data": {
        "message": "P1 INCIDENT: {{service}} - {{summary}}\nOncall response: {{oncall_response}}",
        "target_channel_id": "ops-alerts",
        "target_platform": "slack",
        "label": "Notify Ops (P1)"
      },
      "position": { "x": 1250, "y": 0 }
    },
    {
      "id": "escalate_manager",
      "type": "action",
      "data": {
        "actionType": "human_handoff",
        "reason": "P1 incident on {{service}}: {{summary}}",
        "label": "Escalate to Eng Manager"
      },
      "position": { "x": 1250, "y": 120 }
    },
    {
      "id": "delegate_p2",
      "type": "delegate_to_bot",
      "data": {
        "target_bot_id": "bt_oncall_bot",
        "outputVariable": "oncall_response",
        "context": "{\"severity\": \"P2\", \"service\": \"{{service}}\", \"summary\": \"{{summary}}\"}",
        "label": "Oncall Bot (P2)"
      },
      "position": { "x": 1000, "y": 250 }
    },
    {
      "id": "notify_ops_p2",
      "type": "send_proactive",
      "data": {
        "message": "P2 Incident: {{service}} - {{summary}}\nOncall acknowledged.",
        "target_channel_id": "ops-alerts",
        "target_platform": "slack",
        "label": "Notify Ops (P2)"
      },
      "position": { "x": 1250, "y": 250 }
    },
    {
      "id": "create_ticket",
      "type": "mcp_tool",
      "data": {
        "toolName": "jira__create_issue",
        "arguments": "{\"project\": \"OPS\", \"type\": \"Bug\", \"summary\": \"[P3] {{service}}: {{summary}}\", \"priority\": \"Low\"}",
        "outputVariable": "ticket",
        "label": "Create Ticket (P3)"
      },
      "position": { "x": 1000, "y": 400 }
    },
    {
      "id": "ack_p3",
      "type": "send_message",
      "data": {
        "message": "P3 alert logged. Ticket created: {{ticket.key}}. Service: {{service}}.",
        "label": "Acknowledge"
      },
      "position": { "x": 1250, "y": 400 }
    }
  ],
  "edges": [
    { "id": "e1", "source": "trigger", "target": "parse" },
    { "id": "e2", "source": "parse", "target": "extract" },
    { "id": "e3", "source": "extract", "target": "route" },
    { "id": "e4", "source": "route", "target": "delegate_p1", "sourceHandle": "case_P1" },
    { "id": "e5", "source": "delegate_p1", "target": "notify_ops_p1" },
    { "id": "e6", "source": "delegate_p1", "target": "escalate_manager" },
    { "id": "e7", "source": "route", "target": "delegate_p2", "sourceHandle": "case_P2" },
    { "id": "e8", "source": "delegate_p2", "target": "notify_ops_p2" },
    { "id": "e9", "source": "route", "target": "create_ticket", "sourceHandle": "case_default" },
    { "id": "e10", "source": "create_ticket", "target": "ack_p3" }
  ]
}

Importing a Recipe

To use any of these recipes, copy the workflow JSON and set it on your bot:

Via Dashboard

  1. Open your bot’s detail page
  2. Go to the Workflow tab
  3. Paste the JSON into the workflow editor
  4. Toggle Enable Workflow

Via API

curl -X PATCH https://api.aerostack.dev/api/bots/YOUR_BOT_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_json": "{...paste the JSON here...}",
    "workflow_enabled": 1
  }'

These recipes use multiple llm_call nodes. Each LLM call incurs token costs. For cost-sensitive deployments, consider using a cheaper model (like gpt-4o-mini or gemini-2.5-flash) in the bot configuration, or use BYOK billing mode.

⚠️

Replace MCP tool names (like orders__get_order, stripe__create_refund) with the actual tool names from your workspace. The tools shown here are examples — your MCP servers may use different naming conventions.