Sample Flow: AI Support Bot
An AI-powered support ticket workflow that classifies issues and routes them to the appropriate team with an automated response.
Overview
What it does:
- Receives an incoming support request (customer name, email, message)
- Uses an AI Task to classify the issue and generate an initial response
- Routes to the correct team (billing, technical, or general support) based on classification
- Sends a confirmation email to the customer with the AI-generated response
- Logs the ticket to the database
Execution time: 5–15 seconds (depends on LLM response speed)
Prerequisites
Before importing this workflow, set up:
| Requirement | Type | Where to Create |
|---|---|---|
| OpenAI GPT-4o | AI Provider Integration | Connections → Add Connection |
| Company email provider | Email Integration | Connections → Add Connection |
| Tickets database | Database Integration | Connections → Add Connection |
OPENAI_API_KEY | Secret | Secrets → Add Secret |
Update the placeholder IDs in the workflow after import:
int_openai_placeholder→ your AI provider integration IDint_email_placeholder→ your email integration IDint_db_placeholder→ your database integration ID
Input Variables
Trigger this workflow with:
{
"processId": "proc_ai_support_bot",
"data": {
"customerName": "Jane Smith",
"customerEmail": "jane@example.com",
"ticketMessage": "I was charged twice for my subscription this month. Please help.",
"ticketId": "TKT-5521"
}
}
| Variable | Required | Description |
|---|---|---|
customerName | Yes | Customer's full name |
customerEmail | Yes | Customer's email address |
ticketMessage | Yes | The support request text |
ticketId | Yes | Unique ticket identifier |
Workflow Steps
[Start Event]
↓
[AI Task: Classify & Respond]
→ Classification: billing / technical / account / general
→ Priority: low / medium / high / critical
→ AI response draft
↓
[Script Task: Validate Classification]
→ Ensures valid classification, sets defaults
↓
[If/Else: Is billing?]
↓ trueFlow
[Email: Billing Team Alert]
↓ falseFlow
[If/Else: Is technical?]
↓ trueFlow
[Email: Technical Team Alert]
↓ falseFlow
[Email: General Support Alert]
↓ (all paths merge)
[SQL: Log Ticket to Database]
↓
[Email: Customer Confirmation]
↓
[Output Node: Completion]
↓
[End Event]
Node Configuration Details
AI Task: Classify & Respond
aiProviderConnectionId: int_openai_placeholder
systemPrompt: You are a customer support classification expert for {companyName}.
Your job is to analyze support tickets, classify them, assess priority, and draft an empathetic response.
Always respond with valid JSON only.
prompt: Analyze this support ticket and respond with JSON:
Customer: {customerName}
Email: {customerEmail}
Message: {ticketMessage}
Respond with:
{
"classification": "billing|technical|account|general",
"priority": "low|medium|high|critical",
"needsHuman": true|false,
"estimatedResolutionHours": <number>,
"draftResponse": "<empathetic, helpful response to the customer>"
}
Output variables after this node: {classification}, {priority}, {needsHuman}, {estimatedResolutionHours}, {draftResponse}
Script Task: Validate Classification
const validClassifications = ['billing', 'technical', 'account', 'general'];
const validPriorities = ['low', 'medium', 'high', 'critical'];
const safeClassification = validClassifications.includes(classification)
? classification
: 'general';
const safePriority = validPriorities.includes(priority)
? priority
: 'medium';
return {
classification: safeClassification,
priority: safePriority,
teamEmail: safeClassification === 'billing'
? 'billing@yourcompany.com'
: safeClassification === 'technical'
? 'tech@yourcompany.com'
: 'support@yourcompany.com'
};
Email: Customer Confirmation
mailConnectionId: int_email_placeholder
recipientAddress: {customerEmail}
subject: RE: Your Support Request #{ticketId}
contentType: text/html
body:
<p>Dear {customerName},</p>
<p>Thank you for reaching out to us. We've received your support request and our {classification} team will look into this for you.</p>
<p><strong>Our initial response:</strong></p>
<blockquote>{draftResponse}</blockquote>
<p><strong>Priority:</strong> {priority}<br>
<strong>Estimated resolution:</strong> {estimatedResolutionHours} hours<br>
<strong>Ticket ID:</strong> {ticketId}</p>
<p>We'll follow up with you shortly.</p>
<p>Best regards,<br>Support Team</p>
SQL: Log Ticket
INSERT INTO support_tickets
(ticket_id, customer_email, customer_name, message, classification, priority, needs_human, draft_response, created_at)
VALUES
('{ticketId}', '{customerEmail}', '{customerName}', '{ticketMessage}',
'{classification}', '{priority}', {needsHuman}, '{draftResponse}', NOW())
Output
After completion, the process instance data contains:
{
"customerName": "Jane Smith",
"customerEmail": "jane@example.com",
"ticketMessage": "I was charged twice...",
"ticketId": "TKT-5521",
"classification": "billing",
"priority": "high",
"needsHuman": true,
"estimatedResolutionHours": 4,
"draftResponse": "Dear Jane, I understand how frustrating it must be...",
"teamEmail": "billing@yourcompany.com",
"emailSent": true
}
Full Workflow JSON
Import this JSON into apptor flow. Remember to replace placeholder IDs.
{
"nodes": [
{
"nodeId": "start-1",
"name": "Start",
"nodeType": "startEvent"
},
{
"nodeId": "ai-classify-1",
"name": "Classify and Draft Response",
"nodeType": "aiTask",
"properties": {
"aiProviderConnectionId": "int_openai_placeholder",
"systemPrompt": "You are a customer support classification expert. Always respond with valid JSON only.",
"prompt": "Analyze this support ticket:\nCustomer: {customerName}\nMessage: {ticketMessage}\n\nRespond with JSON:\n{\"classification\": \"billing|technical|account|general\", \"priority\": \"low|medium|high|critical\", \"needsHuman\": true, \"estimatedResolutionHours\": 4, \"draftResponse\": \"<helpful response>\"}"
},
"timeout": { "duration": 30, "durationUom": "SECONDS", "action": "FAIL" },
"retry": { "maxAttempts": 2, "delay": 5, "delayUom": "SECONDS" }
},
{
"nodeId": "script-validate-1",
"name": "Validate Classification",
"nodeType": "scriptTask",
"properties": {
"scriptFormat": "javascript",
"script": "const valid = ['billing','technical','account','general']; const c = valid.includes(classification) ? classification : 'general'; const teamMap = {billing:'billing@yourcompany.com', technical:'tech@yourcompany.com'}; return { classification: c, teamEmail: teamMap[c] || 'support@yourcompany.com' };"
}
},
{
"nodeId": "ifelse-billing-1",
"name": "Is Billing?",
"nodeType": "ifElse"
},
{
"nodeId": "ifelse-technical-1",
"name": "Is Technical?",
"nodeType": "ifElse"
},
{
"nodeId": "email-billing-team",
"name": "Alert Billing Team",
"nodeType": "serviceTask",
"properties": {
"mailConnectionId": "int_email_placeholder",
"recipientAddress": "billing@yourcompany.com",
"subject": "New BILLING Ticket #{ticketId} - Priority: {priority}",
"body": "New billing ticket from {customerName} ({customerEmail}):\n\n{ticketMessage}\n\nAI Draft Response:\n{draftResponse}"
}
},
{
"nodeId": "email-technical-team",
"name": "Alert Technical Team",
"nodeType": "serviceTask",
"properties": {
"mailConnectionId": "int_email_placeholder",
"recipientAddress": "tech@yourcompany.com",
"subject": "New TECHNICAL Ticket #{ticketId} - Priority: {priority}",
"body": "New technical ticket from {customerName} ({customerEmail}):\n\n{ticketMessage}\n\nAI Draft Response:\n{draftResponse}"
}
},
{
"nodeId": "email-general-team",
"name": "Alert General Support",
"nodeType": "serviceTask",
"properties": {
"mailConnectionId": "int_email_placeholder",
"recipientAddress": "support@yourcompany.com",
"subject": "New Ticket #{ticketId} - Priority: {priority}",
"body": "New support ticket from {customerName} ({customerEmail}):\n\n{ticketMessage}\n\nAI Draft Response:\n{draftResponse}"
}
},
{
"nodeId": "sql-log-ticket",
"name": "Log Ticket to Database",
"nodeType": "serviceTask",
"properties": {
"databaseConnectionId": "int_db_placeholder",
"mode": "direct",
"queries": ["INSERT INTO support_tickets (ticket_id, customer_email, customer_name, message, classification, priority, created_at) VALUES ('{ticketId}', '{customerEmail}', '{customerName}', '{ticketMessage}', '{classification}', '{priority}', NOW()) ON CONFLICT (ticket_id) DO NOTHING"]
}
},
{
"nodeId": "email-customer-1",
"name": "Send Customer Confirmation",
"nodeType": "serviceTask",
"properties": {
"mailConnectionId": "int_email_placeholder",
"recipientAddress": "{customerEmail}",
"subject": "RE: Your Support Request #{ticketId}",
"contentType": "text/html",
"body": "<p>Dear {customerName},</p><p>Thank you for reaching out. Our team will assist you shortly.</p><p><strong>Our response:</strong><br>{draftResponse}</p><p>Ticket ID: {ticketId}</p>"
}
},
{
"nodeId": "end-1",
"name": "End",
"nodeType": "endEvent"
}
],
"links": [
{ "linkId": "l1", "sourceId": "start-1", "targetId": "ai-classify-1", "type": "sequenceFlow" },
{ "linkId": "l2", "sourceId": "ai-classify-1", "targetId": "script-validate-1", "type": "sequenceFlow" },
{ "linkId": "l3", "sourceId": "script-validate-1", "targetId": "ifelse-billing-1", "type": "sequenceFlow" },
{ "linkId": "l4", "sourceId": "ifelse-billing-1", "targetId": "email-billing-team", "type": "trueFlow", "condition": { "expression": "${classification == 'billing'}", "language": "juel" } },
{ "linkId": "l5", "sourceId": "ifelse-billing-1", "targetId": "ifelse-technical-1", "type": "falseFlow", "condition": { "expression": "${classification != 'billing'}", "language": "juel" } },
{ "linkId": "l6", "sourceId": "ifelse-technical-1", "targetId": "email-technical-team", "type": "trueFlow", "condition": { "expression": "${classification == 'technical'}", "language": "juel" } },
{ "linkId": "l7", "sourceId": "ifelse-technical-1", "targetId": "email-general-team", "type": "falseFlow", "condition": { "expression": "${classification != 'technical'}", "language": "juel" } },
{ "linkId": "l8", "sourceId": "email-billing-team", "targetId": "sql-log-ticket", "type": "sequenceFlow" },
{ "linkId": "l9", "sourceId": "email-technical-team", "targetId": "sql-log-ticket", "type": "sequenceFlow" },
{ "linkId": "l10", "sourceId": "email-general-team", "targetId": "sql-log-ticket", "type": "sequenceFlow" },
{ "linkId": "l11", "sourceId": "sql-log-ticket", "targetId": "email-customer-1", "type": "sequenceFlow" },
{ "linkId": "l12", "sourceId": "email-customer-1", "targetId": "end-1", "type": "sequenceFlow" }
]
}
Build This Yourself
Follow these steps to recreate this workflow in the designer:
- Navigate to
/flows→ click New Flow → name it "AI Support Bot" → Create - From the Action Browser, drag onto canvas in order:
- Start Event
- AI Task (name: "Classify and Draft Response")
- Script Task (name: "Validate Classification")
- If/Else (name: "Is Billing?")
- If/Else (name: "Is Technical?") — connect from the falseFlow of the first If/Else
- Service Task – Email × 3 (one per team: billing, technical, general)
- Service Task – SQL (name: "Log Ticket to Database")
- Service Task – Email (name: "Send Customer Confirmation")
- End Event
- Connect nodes: Start → AI Task → Script Task → If/Else (Billing) → ...
- If/Else (Billing) trueFlow → Email Billing Team
- If/Else (Billing) falseFlow → If/Else (Technical)
- If/Else (Technical) trueFlow → Email Technical Team
- If/Else (Technical) falseFlow → Email General Support
- All three email nodes → SQL Log → Customer Confirmation Email → End
- Configure the AI Task: select your AI Provider connection, paste the system prompt and user prompt from the Node Configuration section above
- Configure each If/Else condition (Properties Panel → Condition field):
- Is Billing? →
${classification == 'billing'} - Is Technical? →
${classification == 'technical'}
- Is Billing? →
- Configure each Email node with the subject and body templates from above, using
{variableName}syntax - Configure the SQL node with your database connection and the INSERT query
- Click Save → Publish
- Test by clicking Run and entering sample input data (customerName, customerEmail, ticketMessage, ticketId)