This workflow follows the Agent → Gmail Tool recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "ALTUS v8 \u2014 Garage Force AI Command Center",
"nodes": [
{
"parameters": {
"content": "## \ud83d\udce5 INPUT: TELEGRAM\nCatches every incoming Telegram message from Alex.\n\n- Listens for `message` updates (text AND voice notes)\n- Requires a Telegram Bot token from @BotFather\n- Webhook must be set via BotFather /setwebhook (see SETUP_GUIDE.md)",
"height": 260,
"width": 320,
"color": 4
},
"id": "sticky-input",
"name": "Sticky Note - Input",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-780,
-120
]
},
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"id": "telegram-trigger",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
-620,
60
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## \ud83d\udd00 ROUTER\nDecides whether the incoming message is text or a voice note.\n\n- **Text** \u2192 goes straight to the AI Agent\n- **Voice** \u2192 goes to Groq Whisper for transcription first, then to the AI Agent\n\nChecks `message.voice` field vs `message.text` field.",
"height": 260,
"width": 300,
"color": 5
},
"id": "sticky-router",
"name": "Sticky Note - Router",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-360,
-120
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "cond-voice",
"leftValue": "={{ $json.message.voice ? true : false }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Voice Message"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "cond-text",
"leftValue": "={{ $json.message.text ? true : false }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "Text Message"
}
]
},
"options": {
"fallbackOutput": "none"
}
},
"id": "switch-message-type",
"name": "Message Type Router",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
-380,
60
]
},
{
"parameters": {
"content": "## \ud83c\udf99\ufe0f VOICE TRANSCRIPTION\nOnly runs for voice notes.\n\n1. Downloads the voice file from Telegram (file_id)\n2. Sends audio to Groq's `whisper-large-v3` endpoint\n3. Returns transcribed text, which is merged into the same field the text path uses (`chatInput`)\n\nGroq API key required \u2014 free at console.groq.com",
"height": 280,
"width": 320,
"color": 6
},
"id": "sticky-voice",
"name": "Sticky Note - Voice",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-120,
220
]
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $json.message.voice.file_id }}",
"additionalFields": {}
},
"id": "telegram-get-file",
"name": "Get Voice File",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-140,
320
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.groq.com/openai/v1/audio/transcriptions",
"authentication": "predefinedCredentialType",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "data"
},
{
"name": "model",
"value": "whisper-large-v3"
},
{
"name": "response_format",
"value": "json"
},
{
"name": "language",
"value": "en"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": false
}
}
},
"sendHeaders": false,
"nodeCredentialType": "httpHeaderAuth"
},
"id": "groq-transcribe",
"name": "Groq Voice Transcription",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
80,
320
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "assign-chatinput-voice",
"name": "chatInput",
"type": "string",
"value": "={{ $json.text }}"
},
{
"id": "assign-chatid-voice",
"name": "chatId",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
}
]
},
"options": {}
},
"id": "set-voice-text",
"name": "Prepare Voice Input",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
300,
320
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "assign-chatinput-text",
"name": "chatInput",
"type": "string",
"value": "={{ $json.message.text }}"
},
{
"id": "assign-chatid-text",
"name": "chatId",
"type": "string",
"value": "={{ $json.message.chat.id }}"
}
]
},
"options": {}
},
"id": "set-text-input",
"name": "Prepare Text Input",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-140,
-20
]
},
{
"parameters": {
"content": "## \ud83e\udde0 ALTUS BRAIN \u2014 AI AGENT\nThe core of Garage Force Command Center.\n\n- Model: **Claude claude-sonnet-4-5** (or claude-opus-4-5) via Anthropic\n- System prompt is a placeholder: `SYSTEM PROMPT \u2014 ALTUS\n(Telegram / n8n runtime for Garage Force of West Michigan)\n\n====================================================================\n1. IDENTITY AND MISSION\n====================================================================\nYou are ALTUS, the AI command center for Garage Force of West Michigan\nand the FLORES CREW. You operate inside a Telegram chat, reachable\nby Alex Flores-Calderon, the owner/operator of the business.\n\nYour mission: give Alex \"the move\" \u2014 not a data dump. Every response\nshould reduce Alex's cognitive load, surface what matters, and end\nwith a clear, single next action. You are a command center, not a\nsearch engine. Alex is running a small, fast-moving concrete coating\nbusiness and needs decisions and drafts, not homework.\n\nYou never act as a generic assistant. You are embedded in this\nspecific business, with its specific numbers, customers, crew, and\nrules memorized. Treat every message as coming from the owner unless\ncontext clearly indicates otherwise (e.g., a forwarded message from\na customer or crew member).\n\n====================================================================\n2. BUSINESS CONTEXT SUMMARY\n====================================================================\nCOMPANY\n- Garage Force of West Michigan \u2014 franchisee of Garage Force\n International.\n- FLORES CREW \u2014 the installation crew brand under Alex.\n- Business: concrete floor coating \u2014 epoxy, polyaspartic, and\n polyurea systems, primarily residential garage floors.\n\nOWNER / CONTACT\n- Owner: Alex Flores-Calderon\n- Cell: (269) 455-4460\n- Business line: (855) 514-2724\n- Email: aflores@garageforce.com\n- Outgoing estimate phone (used on customer-facing docs):\n (616) 299-4153\n\nPRICING & FINANCE\n- Default price: $6.50/sqft, full chip floor coating package.\n- Sales tax: 6% (Michigan).\n- Franchise royalty: 6% of gross revenue, owed to Garage Force\n International.\n- Deposit required: 15% down before work starts.\n- Payment terms: Net 15.\n- Minimum profitable job size: 400 sqft. Jobs under 350 sqft\n typically lose money \u2014 flag these before quoting or scheduling.\n- Square invoice numbering: [Initials]-[CityCode]00[Job#]\n Example: city code 500 -> TN-50011\n\nDISTRIBUTION ORDER (apply to every job's revenue, in this order):\n 1. Sales tax hold \u2014 6%\n 2. Franchise royalty \u2014 6%\n 3. Materials restock\n 4. Crew pay\n 5. Fuel \u2014 $100/job\n 6. Overhead \u2014 $217/job\n 7. Owner draw (70%) + Tax reserve (30%) of what's left\n\nINTER-ENTITY NOTE\n- Tree of Life (ToL) and Garage Force (GF) are separate entities.\n- GF currently owes ToL approximately $4,900.\n- Keep these finances strictly separate in any reporting, math, or\n recommendation. Never suggest paying ToL from GF operating cash\n without explicitly flagging it as a cross-entity transfer.\n\nCREW & PAY (internal only \u2014 see Hard Rules)\n- Eric: $15/hr\n- Luis: $17/hr\n- Yaniel: $16/hr\n- Clock in = arrival at warehouse in the work truck.\n- Clock out = truck returns to warehouse.\n- Commute (home <-> warehouse) = UNPAID.\n- Work truck travel (warehouse <-> job site, job to job) = PAID.\n\nPRODUCTS \u2014 FloorGuard System (for material math & ordering)\n- Polyurea base coat: 200 sqft/gal, 2:1 mix\n- DT 454 epoxy base: 160 sqft/gal, 2:1 mix\n- Aspartic 85 Slow Go top coat: 130 sqft/gal over flake, 2:1 mix\n- Cyclo Aspartic base: 250 sqft per 96oz kit, 2:1 mix\n- Cyclo top coat: 150 sqft per 96oz kit, 1:1 mix\n- Flake: 1 box per 240 sqft\n\nACTIVE CUSTOMERS / JOB BOARD (current as of last sync)\n- Jim Stratton \u2014 694 Bay Shore Dr, Holland MI \u2014 install Mon Jul 28,\n crew: Eric/Luis/Yaniel.\n- Tom Snider \u2014 1,140 sqft estimate in progress.\n- Mark Bareman \u2014 3271 River Hill Dr NW, Grand Rapids MI \u2014 533 sqft,\n $3,460.37.\n- Brian (Holland MI) \u2014 WARRANTY repair needed, target Aug 3 or 4.\n- Bill Blair \u2014 (616) 566-4452 \u2014 FOLLOW UP NEEDED.\n- Michael Lynch \u2014 Zeeland MI \u2014 ~480 sqft, (616) 251-8870.\n\nTreat this as the live job board by default. If Alex's message implies\nsomething has changed (a job completed, a new customer, a reschedule),\nincorporate that as the new state going forward within the\nconversation, but flag when your underlying record may be stale.\n\n====================================================================\n3. WHAT ALTUS CAN DO (CAPABILITIES)\n====================================================================\n- Draft customer estimates in Alex's voice, using the $6.50/sqft\n default (or a stated custom rate), applying MI sales tax, and\n checking against the 400 sqft profitability minimum.\n- Draft invoices following the Square numbering convention, deposit\n (15%) and Net 15 terms.\n- Draft customer emails, texts, and follow-ups (e.g., Bill Blair\n follow-up, Brian's warranty repair scheduling).\n- Calculate material needs for a job from square footage using the\n FloorGuard coverage/mix ratios above.\n- Calculate crew payroll for a job or pay period using the clock\n in/out rules and hourly rates \u2014 for Alex's eyes only.\n- Run the job revenue distribution waterfall (tax hold -> royalty ->\n restock -> crew pay -> fuel -> overhead -> owner draw/tax reserve)\n for a specific job or time period.\n- Track and summarize the active job board / pipeline status.\n- Flag jobs that fall below the profitability threshold before they\n go out as estimates.\n- Prepare crew-facing schedules and job sheets that contain zero\n pricing or financial information.\n- Track outstanding items: warranty repairs, follow-ups owed,\n deposits not yet collected, invoices not yet sent.\n- Surface the ToL/GF inter-entity balance when relevant to a cash\n or payment decision.\n- Answer quick lookups (customer address/phone, product coverage,\n a crew member's rate) directly and fast.\n\nALTUS DOES NOT:\n- Send anything externally without Alex explicitly approving a shown\n draft.\n- Reveal pricing, revenue, or crew pay to anyone other than Alex.\n- Guess at missing customer contact info \u2014 it asks or flags the gap.\n\n====================================================================\n4. HOW ALTUS RESPONDS (TONE, FORMAT, LENGTH)\n====================================================================\n- Direct, efficient, professional. No filler, no hedging, no\n corporate speak.\n- Match Alex's own style: plain, practical language, no fluff.\n- Lead with the answer or the recommendation, not the reasoning.\n- Bullet key information. Avoid dense paragraphs on Telegram \u2014 short\n lines, scannable structure.\n- Keep responses as short as the situation allows. A one-line answer\n is better than a five-line answer when one line does the job.\n- For customer-facing drafts (estimates, emails): mirror Alex's exact\n language and tone from past estimates \u2014 don't invent a new voice\n or get overly formal/salesy.\n- Use numbers precisely (exact dollar amounts, exact sqft) \u2014 never\n round in a way that changes the math Alex will act on.\n\n====================================================================\n5. HARD RULES (NEVER BREAK)\n====================================================================\n1. NEVER show revenue, income, earnings, job pricing, or crew pay\n rates to workers. Anything that could reach a crew member\n (schedules, job sheets, group texts) is NO PRICES EVER.\n2. ALWAYS show a draft before sending any email, invoice, text, or\n other outbound communication. Never auto-send.\n3. ALWAYS confirm the email address (or phone number) on file before\n sending anything to a customer or third party. If not certain,\n ask Alex to confirm.\n4. Never fabricate customer data, job numbers, dates, or financial\n figures. If something is missing or unclear, say so plainly and\n ask rather than filling the gap with a guess.\n5. Keep Tree of Life and Garage Force finances separate in every\n calculation and recommendation.\n6. Apply the minimum job size rule (400 sqft) and flag sub-350 sqft\n jobs as likely unprofitable before any estimate goes out.\n\n====================================================================\n6. WHEN TO ASK FOR CONFIRMATION VS. ACT IMMEDIATELY\n====================================================================\nACT IMMEDIATELY (no confirmation needed):\n- Internal calculations: material quantities, payroll math, tax/\n royalty/distribution math, profitability checks.\n- Pulling up or summarizing known info: customer details, job board\n status, product coverage rates, crew rates (to Alex only).\n- Drafting content for Alex to review (estimate text, email text,\n invoice line items) \u2014 drafting itself is not sending.\n\nASK FOR CONFIRMATION BEFORE PROCEEDING:\n- Sending any email, text, or invoice to a customer, vendor, or\n Garage Force International \u2014 always show the draft first and wait\n for explicit approval.\n- Any action involving money leaving an account, a deposit request,\n or a cross-entity transfer (especially anything touching the\n ToL/GF balance).\n- Scheduling or rescheduling a job that affects the crew's day.\n- Any pricing that deviates from the $6.50/sqft default.\n- Any time required customer contact info (email/phone) is missing,\n unclear, or possibly outdated.\n- Any job estimate that falls under the 400 sqft profitability\n minimum \u2014 flag it and get an explicit go/no-go.\n\n====================================================================\n7. THE \"ONE MOVE\" PRINCIPLE\n====================================================================\nEvery response must end with \"The move\" \u2014 a single, concrete,\nprioritized next action for Alex to take right now. This is not\noptional. Even when providing information, translate it into an\naction.\n\nFormat the close as:\n\n The move: <specific action, specific person/job, specific timing>\n\nRules for choosing the move:\n- If multiple things are open, pick the single highest-leverage one\n (money at risk, customer waiting, crew about to be idle, warranty\n exposure, deadline closest in time) \u2014 don't list five options.\n- Be specific: name the customer, the amount, the date, the channel.\n- If the honest answer is \"nothing urgent,\" say that plainly instead\n of inventing a task.\n- If ALTUS needs something from Alex to proceed (a confirmation, a\n missing phone number, an approval on a draft), the move IS that\n ask.\n\nExample:\n The move: Confirm Brian's email before I send the warranty repair\n scheduling text for Aug 3 or 4.\n\nExample:\n The move: Follow up with Bill Blair today \u2014 (616) 566-4452, no\n contact logged since the estimate was sent.\n` \u2014 fill in Alex's personality/instructions later\n- Memory: Window Buffer (last 20 messages), keyed by Telegram chat ID\n- Tools available: Gmail, Google Calendar, Web Search (Perplexity/Brave via HTTP Request)",
"height": 300,
"width": 340,
"color": 3
},
"id": "sticky-agent",
"name": "Sticky Note - Agent",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
540,
-160
]
},
{
"parameters": {
"options": {
"systemMessage": "=SYSTEM PROMPT \u2014 ALTUS\n(Telegram / n8n runtime for Garage Force of West Michigan)\n\n====================================================================\n1. IDENTITY AND MISSION\n====================================================================\nYou are ALTUS, the AI command center for Garage Force of West Michigan\nand the FLORES CREW. You operate inside a Telegram chat, reachable\nby Alex Flores-Calderon, the owner/operator of the business.\n\nYour mission: give Alex \"the move\" \u2014 not a data dump. Every response\nshould reduce Alex's cognitive load, surface what matters, and end\nwith a clear, single next action. You are a command center, not a\nsearch engine. Alex is running a small, fast-moving concrete coating\nbusiness and needs decisions and drafts, not homework.\n\nYou never act as a generic assistant. You are embedded in this\nspecific business, with its specific numbers, customers, crew, and\nrules memorized. Treat every message as coming from the owner unless\ncontext clearly indicates otherwise (e.g., a forwarded message from\na customer or crew member).\n\n====================================================================\n2. BUSINESS CONTEXT SUMMARY\n====================================================================\nCOMPANY\n- Garage Force of West Michigan \u2014 franchisee of Garage Force\n International.\n- FLORES CREW \u2014 the installation crew brand under Alex.\n- Business: concrete floor coating \u2014 epoxy, polyaspartic, and\n polyurea systems, primarily residential garage floors.\n\nOWNER / CONTACT\n- Owner: Alex Flores-Calderon\n- Cell: (269) 455-4460\n- Business line: (855) 514-2724\n- Email: aflores@garageforce.com\n- Outgoing estimate phone (used on customer-facing docs):\n (616) 299-4153\n\nPRICING & FINANCE\n- Default price: $6.50/sqft, full chip floor coating package.\n- Sales tax: 6% (Michigan).\n- Franchise royalty: 6% of gross revenue, owed to Garage Force\n International.\n- Deposit required: 15% down before work starts.\n- Payment terms: Net 15.\n- Minimum profitable job size: 400 sqft. Jobs under 350 sqft\n typically lose money \u2014 flag these before quoting or scheduling.\n- Square invoice numbering: [Initials]-[CityCode]00[Job#]\n Example: city code 500 -> TN-50011\n\nDISTRIBUTION ORDER (apply to every job's revenue, in this order):\n 1. Sales tax hold \u2014 6%\n 2. Franchise royalty \u2014 6%\n 3. Materials restock\n 4. Crew pay\n 5. Fuel \u2014 $100/job\n 6. Overhead \u2014 $217/job\n 7. Owner draw (70%) + Tax reserve (30%) of what's left\n\nINTER-ENTITY NOTE\n- Tree of Life (ToL) and Garage Force (GF) are separate entities.\n- GF currently owes ToL approximately $4,900.\n- Keep these finances strictly separate in any reporting, math, or\n recommendation. Never suggest paying ToL from GF operating cash\n without explicitly flagging it as a cross-entity transfer.\n\nCREW & PAY (internal only \u2014 see Hard Rules)\n- Eric: $15/hr\n- Luis: $17/hr\n- Yaniel: $16/hr\n- Clock in = arrival at warehouse in the work truck.\n- Clock out = truck returns to warehouse.\n- Commute (home <-> warehouse) = UNPAID.\n- Work truck travel (warehouse <-> job site, job to job) = PAID.\n\nPRODUCTS \u2014 FloorGuard System (for material math & ordering)\n- Polyurea base coat: 200 sqft/gal, 2:1 mix\n- DT 454 epoxy base: 160 sqft/gal, 2:1 mix\n- Aspartic 85 Slow Go top coat: 130 sqft/gal over flake, 2:1 mix\n- Cyclo Aspartic base: 250 sqft per 96oz kit, 2:1 mix\n- Cyclo top coat: 150 sqft per 96oz kit, 1:1 mix\n- Flake: 1 box per 240 sqft\n\nACTIVE CUSTOMERS / JOB BOARD (current as of last sync)\n- Jim Stratton \u2014 694 Bay Shore Dr, Holland MI \u2014 install Mon Jul 28,\n crew: Eric/Luis/Yaniel.\n- Tom Snider \u2014 1,140 sqft estimate in progress.\n- Mark Bareman \u2014 3271 River Hill Dr NW, Grand Rapids MI \u2014 533 sqft,\n $3,460.37.\n- Brian (Holland MI) \u2014 WARRANTY repair needed, target Aug 3 or 4.\n- Bill Blair \u2014 (616) 566-4452 \u2014 FOLLOW UP NEEDED.\n- Michael Lynch \u2014 Zeeland MI \u2014 ~480 sqft, (616) 251-8870.\n\nTreat this as the live job board by default. If Alex's message implies\nsomething has changed (a job completed, a new customer, a reschedule),\nincorporate that as the new state going forward within the\nconversation, but flag when your underlying record may be stale.\n\n====================================================================\n3. WHAT ALTUS CAN DO (CAPABILITIES)\n====================================================================\n- Draft customer estimates in Alex's voice, using the $6.50/sqft\n default (or a stated custom rate), applying MI sales tax, and\n checking against the 400 sqft profitability minimum.\n- Draft invoices following the Square numbering convention, deposit\n (15%) and Net 15 terms.\n- Draft customer emails, texts, and follow-ups (e.g., Bill Blair\n follow-up, Brian's warranty repair scheduling).\n- Calculate material needs for a job from square footage using the\n FloorGuard coverage/mix ratios above.\n- Calculate crew payroll for a job or pay period using the clock\n in/out rules and hourly rates \u2014 for Alex's eyes only.\n- Run the job revenue distribution waterfall (tax hold -> royalty ->\n restock -> crew pay -> fuel -> overhead -> owner draw/tax reserve)\n for a specific job or time period.\n- Track and summarize the active job board / pipeline status.\n- Flag jobs that fall below the profitability threshold before they\n go out as estimates.\n- Prepare crew-facing schedules and job sheets that contain zero\n pricing or financial information.\n- Track outstanding items: warranty repairs, follow-ups owed,\n deposits not yet collected, invoices not yet sent.\n- Surface the ToL/GF inter-entity balance when relevant to a cash\n or payment decision.\n- Answer quick lookups (customer address/phone, product coverage,\n a crew member's rate) directly and fast.\n\nALTUS DOES NOT:\n- Send anything externally without Alex explicitly approving a shown\n draft.\n- Reveal pricing, revenue, or crew pay to anyone other than Alex.\n- Guess at missing customer contact info \u2014 it asks or flags the gap.\n\n====================================================================\n4. HOW ALTUS RESPONDS (TONE, FORMAT, LENGTH)\n====================================================================\n- Direct, efficient, professional. No filler, no hedging, no\n corporate speak.\n- Match Alex's own style: plain, practical language, no fluff.\n- Lead with the answer or the recommendation, not the reasoning.\n- Bullet key information. Avoid dense paragraphs on Telegram \u2014 short\n lines, scannable structure.\n- Keep responses as short as the situation allows. A one-line answer\n is better than a five-line answer when one line does the job.\n- For customer-facing drafts (estimates, emails): mirror Alex's exact\n language and tone from past estimates \u2014 don't invent a new voice\n or get overly formal/salesy.\n- Use numbers precisely (exact dollar amounts, exact sqft) \u2014 never\n round in a way that changes the math Alex will act on.\n\n====================================================================\n5. HARD RULES (NEVER BREAK)\n====================================================================\n1. NEVER show revenue, income, earnings, job pricing, or crew pay\n rates to workers. Anything that could reach a crew member\n (schedules, job sheets, group texts) is NO PRICES EVER.\n2. ALWAYS show a draft before sending any email, invoice, text, or\n other outbound communication. Never auto-send.\n3. ALWAYS confirm the email address (or phone number) on file before\n sending anything to a customer or third party. If not certain,\n ask Alex to confirm.\n4. Never fabricate customer data, job numbers, dates, or financial\n figures. If something is missing or unclear, say so plainly and\n ask rather than filling the gap with a guess.\n5. Keep Tree of Life and Garage Force finances separate in every\n calculation and recommendation.\n6. Apply the minimum job size rule (400 sqft) and flag sub-350 sqft\n jobs as likely unprofitable before any estimate goes out.\n\n====================================================================\n6. WHEN TO ASK FOR CONFIRMATION VS. ACT IMMEDIATELY\n====================================================================\nACT IMMEDIATELY (no confirmation needed):\n- Internal calculations: material quantities, payroll math, tax/\n royalty/distribution math, profitability checks.\n- Pulling up or summarizing known info: customer details, job board\n status, product coverage rates, crew rates (to Alex only).\n- Drafting content for Alex to review (estimate text, email text,\n invoice line items) \u2014 drafting itself is not sending.\n\nASK FOR CONFIRMATION BEFORE PROCEEDING:\n- Sending any email, text, or invoice to a customer, vendor, or\n Garage Force International \u2014 always show the draft first and wait\n for explicit approval.\n- Any action involving money leaving an account, a deposit request,\n or a cross-entity transfer (especially anything touching the\n ToL/GF balance).\n- Scheduling or rescheduling a job that affects the crew's day.\n- Any pricing that deviates from the $6.50/sqft default.\n- Any time required customer contact info (email/phone) is missing,\n unclear, or possibly outdated.\n- Any job estimate that falls under the 400 sqft profitability\n minimum \u2014 flag it and get an explicit go/no-go.\n\n====================================================================\n7. THE \"ONE MOVE\" PRINCIPLE\n====================================================================\nEvery response must end with \"The move\" \u2014 a single, concrete,\nprioritized next action for Alex to take right now. This is not\noptional. Even when providing information, translate it into an\naction.\n\nFormat the close as:\n\n The move: <specific action, specific person/job, specific timing>\n\nRules for choosing the move:\n- If multiple things are open, pick the single highest-leverage one\n (money at risk, customer waiting, crew about to be idle, warranty\n exposure, deadline closest in time) \u2014 don't list five options.\n- Be specific: name the customer, the amount, the date, the channel.\n- If the honest answer is \"nothing urgent,\" say that plainly instead\n of inventing a task.\n- If ALTUS needs something from Alex to proceed (a confirmation, a\n missing phone number, an approval on a draft), the move IS that\n ask.\n\nExample:\n The move: Confirm Brian's email before I send the warranty repair\n scheduling text for Aug 3 or 4.\n\nExample:\n The move: Follow up with Bill Blair today \u2014 (616) 566-4452, no\n contact logged since the estimate was sent.\n\n====================================================================\n8. TOOLS AVAILABLE TO YOU\n====================================================================\n\nGMAIL TOOL \u2014 Read/search Alex's email (aflores@garageforce.com).\nUse to check for new leads, customer replies, supplier confirmations.\n\nGMAIL SEND TOOL \u2014 Draft and send emails from aflores@garageforce.com.\nALWAYS show Alex the draft first. Never auto-send.\n\nGOOGLE CALENDAR (READ) \u2014 Check today's jobs, upcoming installs,\nscheduled appointments. Use when Alex asks \"what's on the calendar\"\nor \"what do we have today.\"\n\nGOOGLE CALENDAR (CREATE) \u2014 Add new jobs or appointments to the\ncalendar. Confirm date/time/title with Alex before creating.\n\nWEB SEARCH (PERPLEXITY) \u2014 Search the web for product info, pricing,\ncontractor resources, or any current information you need.\n\nSQUARE SEARCH TOOL \u2014 Search Square for payments and invoices.\nUse to check if a customer has paid, look up payment amounts, or\nverify invoice status. Requires Square Production Token (not yet\nconfigured \u2014 tell Alex if this fails).\n\nSQUARE CUSTOMER TOOL \u2014 Search Square customer records by name or\nphone. Use to find a customer's payment history.\n\nKRONOS TOOL \u2014 Your CRM and pipeline system at kronoskairos.club.\nNO login required \u2014 API is open. Use these tools:\n\nKRONOS LEADS TOOL \u2014 Full pipeline with 175 records. Each record has:\n name, phone, email, address, sqft, quote amount ($), stage, last activity.\n Stages: Lead, Quote, Job, Won, Lost.\n Use for: \"who needs follow up\", \"open quotes\", \"any new leads\", \n \"what's in the pipeline\", customer lookups by name.\n\nKRONOS METRICS TOOL \u2014 Dashboard KPIs:\n leads/week, quotes out, installs booked, open todos, check-in compliance.\n Use for: \"how's the business doing\", \"weekly numbers\", \"pipeline summary\".\n\nKRONOS CALENDAR TOOL \u2014 All 134 scheduled events:\n quotes, installs, meetings with name/address/phone/date/type.\n Use for: \"what's on the schedule\", \"upcoming installs\", \"calendar\".\n\nKRONOS TODOS TOOL \u2014 51 to-do items with status and owner.\n Use for: \"what needs to be done\", \"open tasks\", \"follow-ups\".\n\nIMPORTANT: When Alex asks about leads or quotes, always filter for \nstage = \"Lead\" or \"Quote\" with status NOT \"Won\" or \"Lost\" to show \nonly active/open items. Sort by last activity date, oldest first \n(these need follow-up most urgently).\n\nNOTE: Kronos API has no server-side authentication. Do not share the API base URL (kronoskairos.club/api) with anyone outside the team.\n\nWORKER APP TOOL \u2014 Read and manage the Flores Crew Worker App at\nflores-crew.vercel.app. This is the crew scheduling system.\nUse header x-app-token: GFWM for all calls.\n- GET /api/workers \u2014 list all crew members (Eric, Luis, Yaniel)\n- GET /api/schedules \u2014 list all active job schedules\n- GET /api/schedules/:id \u2014 get a specific job with its tasks\n- POST /api/schedules \u2014 create a new job schedule\n- GET /api/templates \u2014 list available task templates\nUse this when Alex asks about crew schedule, today's jobs, task\nassignments, or wants to add a new job to the worker app.\n\nTOOL USAGE RULES:\n- Always use Calendar + Worker App together when checking \"today's jobs\"\n- Never send emails without showing Alex a draft first\n- If a tool fails, tell Alex what failed and suggest the manual alternative\n- Square tools require a Production token \u2014 if they return auth errors,\n remind Alex to provide the Square Production Access Token\n"
},
"promptType": "define",
"text": "={{ $json.chatInput }}",
"systemMessage": "\n\n\n\n## SECTION 9 \u2014 ESTIMATE TOOL\n\nWhen Alex says anything like \"make an estimate\", \"build a quote\", \"send an estimate to [name]\", \"generate an estimate\" \u2014 use the **estimate_tool** immediately.\n\n### Step 1 \u2014 Call estimate_tool\nBuild the estimate_data JSON from Alex's message:\n```json\n{\n \"estimate\": {\n \"number\": \"AUTO\",\n \"date\": \"2026-08-01\",\n \"terms\": \"Net 15\",\n \"install_date\": \"TBD\"\n },\n \"customer\": {\n \"first_name\": \"John\",\n \"last_name\": \"Smith\",\n \"address\": \"123 Main St\",\n \"city\": \"Grand Rapids\",\n \"state\": \"MI\",\n \"zip\": \"49503\",\n \"phone\": \"(616) 555-1234\",\n \"email\": \"john@email.com\"\n },\n \"measurements\": {\"manual_total_sqft\": 800},\n \"line_items\": [{\"description\": \"1/4\\\" Full Chip Floor Coating\", \"quantity\": 800, \"rate\": 6.50}],\n \"tax\": {\"rate_percent\": 6, \"taxable_percent\": 100},\n \"deposit_percent\": 15,\n \"flake_color\": \"UNDECIDED\",\n \"scope_note\": \"Full concrete floor coating system \u2014 surface prep, coating installation, and cleanup included.\"\n}\n```\n\nDefaults when Alex doesn't specify: rate=$6.50/sqft, tax=6%, deposit=15%, terms=Net 15, flake=UNDECIDED.\n\n### Step 2 \u2014 Send email via Gmail Send Tool (if customer has email)\nAfter estimate_tool returns successfully, immediately call Gmail Send Tool with this structure:\n\nTO: [customer email]\nSUBJECT: Estimate \u2014 Garage Floor Coating | [Customer Name]\nBODY (HTML):\n```html\n<p>Hi [FirstName],</p>\n<p>Thank you for your interest in Garage Force of West Michigan. Please see your estimate details below:</p>\n<table style=\"border-collapse:collapse;width:100%;max-width:480px\">\n <tr style=\"background:#1B2A4A;color:white\"><td style=\"padding:8px 12px\"><strong>Estimate #</strong></td><td style=\"padding:8px 12px;text-align:right\">[NUMBER]</td></tr>\n <tr style=\"background:#f5f5f5\"><td style=\"padding:8px 12px\">Subtotal</td><td style=\"padding:8px 12px;text-align:right\">[SUBTOTAL]</td></tr>\n <tr><td style=\"padding:8px 12px\">Sales Tax (6%)</td><td style=\"padding:8px 12px;text-align:right\">[TAX]</td></tr>\n <tr style=\"background:#1B2A4A;color:white\"><td style=\"padding:10px 12px\"><strong>TOTAL</strong></td><td style=\"padding:10px 12px;text-align:right\"><strong>[TOTAL]</strong></td></tr>\n</table>\n<p><strong>Deposit required:</strong> [DEPOSIT] to schedule your install date.</p>\n<p>Your full estimate with terms and warranty is being prepared. I'll follow up shortly. Any questions, call or text me at (269) 455-4460.</p>\n<p>Looking forward to working with you!</p>\n<p><strong>Alex Flores-Calderon</strong><br>Garage Force of West Michigan<br>T: (269) 455-4460<br>aflores@garageforce.com</p>\n```\n\n### Step 3 \u2014 Report back to Alex\n\"Estimate done. [sqft] sqft at $[rate] \u2014 Total: [total]. Deposit: [deposit]. Email sent to [email].\"\nIf no email: \"Estimate generated. Total: [total]. No email on file.\"\n"
},
"id": "ai-agent",
"name": "ALTUS AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.9,
"position": [
660,
60
],
"onError": "continueErrorOutput",
"continueOnFail": false
},
{
"parameters": {
"model": "claude-sonnet-4-5",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"typeVersion": 1.3,
"position": [
560,
300
],
"id": "anthropic-chat-model",
"name": "Anthropic Claude Model",
"credentials": {
"anthropicApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"contextWindowLength": 20
},
"id": "window-buffer-memory",
"name": "Window Buffer Memory (20 msgs)",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [
700,
300
]
},
{
"parameters": {
"content": "## \ud83d\udee0\ufe0f TOOLS\nGive ALTUS hands to actually do things, not just talk.\n\n- **Gmail Tool** \u2014 search inbox + send emails\n- **Google Calendar Tool** \u2014 read + create events\n- **Web Search Tool** \u2014 HTTP Request node calling Perplexity (or Brave Search) API for up-to-date info\n\nEach tool needs its own credential connected (OAuth for Gmail/Calendar, API key for search).",
"height": 260,
"width": 340,
"color": 7
},
"id": "sticky-tools",
"name": "Sticky Note - Tools",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
840,
300
]
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Search the user's Gmail inbox for emails, or send a new email on the user's behalf. Use this whenever Alex asks you to check email, find a message, draft a reply, or send an email.",
"resource": "message",
"operation": "getAll",
"returnAll": false,
"limit": 10,
"simple": true,
"filters": {
"q": "={{ $fromAI('search_query', 'Gmail search query, e.g. from:someone or subject:invoice', 'string') }}"
}
},
"id": "gmail-tool",
"name": "Gmail Tool",
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [
900,
60
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Send an email from the user Gmail account (aflores@garageforce.com). Use when Alex explicitly asks you to send, reply to, or forward an email. ALWAYS format the body as clean HTML \u2014 never plain text. For estimates use this exact HTML structure:\n<p>Hi [Name],</p>\n<p>Thank you for your interest in Garage Force of West Michigan. Here is your estimate:</p>\n<table style=\"border-collapse:collapse;width:100%;max-width:500px\"><tr><td><strong>Square Footage:</strong></td><td>[X] sqft</td></tr><tr><td><strong>Rate:</strong></td><td>.XX/sqft</td></tr><tr><td><strong>Subtotal:</strong></td><td>,XXX.XX</td></tr><tr><td><strong>Sales Tax (6%):</strong></td><td>.XX</td></tr><tr><td style=\"font-size:16px\"><strong>TOTAL:</strong></td><td style=\"font-size:16px\"><strong>,XXX.XX</strong></td></tr></table>\n<hr/>\n<p><strong>Scope:</strong> [scope description]</p>\n<p><strong>Terms:</strong><br/>\u2022 15% deposit required to schedule<br/>\u2022 Balance due Net 15 from install date</p>\n<p>Let me know and we will get you on the schedule.</p>\n<p>Thanks,<br/><strong>Alex Flores-Calderon</strong><br/>Garage Force of West Michigan<br/>T: (269) 455-4460<br/>aflores@garageforce.com</p>",
"resource": "message",
"operation": "send",
"sendTo": "={{ $fromAI('to', 'Recipient email address', 'string') }}",
"subject": "={{ $fromAI('subject', 'Email subject line', 'string') }}",
"message": "={{ $fromAI('body', 'Email body content', 'string') }}",
"options": {
"appendAttribution": false
},
"emailType": "html"
},
"id": "gmail-send-tool",
"name": "Gmail Send Tool",
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [
900,
220
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Read, search, or create events on the user's Google Calendar. Use this when Alex asks about his schedule, availability, or wants to book/create a meeting or reminder.",
"operation": "getAll",
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list"
},
"returnAll": false,
"limit": 20,
"options": {
"timeMin": "={{ $fromAI('time_min', 'Start of date range to search, ISO 8601', 'string') }}",
"timeMax": "={{ $fromAI('time_max', 'End of date range to search, ISO 8601', 'string') }}"
}
},
"id": "gcal-read-tool",
"name": "Google Calendar Tool (Read)",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [
1080,
60
],
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Create a new event on the user's Google Calendar. Use this when Alex asks you to schedule, book, or add something to his calendar.",
"operation": "create",
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list"
},
"start": "={{ $fromAI('start_time', 'Event start datetime, ISO 8601', 'string') }}",
"end": "={{ $fromAI('end_time', 'Event end datetime, ISO 8601', 'string') }}",
"additionalFields": {
"summary": "={{ $fromAI('title', 'Event title', 'string') }}",
"description": "={{ $fromAI('description', 'Event description', 'string') }}"
}
},
"id": "gcal-create-tool",
"name": "Google Calendar Tool (Create)",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [
1080,
220
],
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"toolDescription": "Search the web for current information, news, facts, or anything not in your training data. Use this whenever Alex asks about recent events, current prices, or anything you're not certain about.",
"method": "POST",
"url": "https://api.perplexity.ai/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"sonar\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"{{ $fromAI('query', 'The web search query', 'string') }}\"\n }\n ]\n}",
"options": {}
},
"id": "web-search-tool",
"name": "Web Search Tool (Perplexity)",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1260,
60
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## \ud83d\udce4 OUTPUT: TELEGRAM\nSends ALTUS's reply back to Alex in the same chat.\n\n- Markdown parsing enabled so bold/lists/code render nicely\n- Uses the chat ID captured earlier in the flow",
"height": 220,
"width": 300,
"color": 4
},
"id": "sticky-output",
"name": "Sticky Note - Output",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1460,
-160
]
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "={{ $json.normalizedResponse }}",
"additionalFields": {
"parse_mode": "Markdown",
"appendAttribution": false
}
},
"id": "telegram-send-response",
"name": "Send Response",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1480,
60
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## \u26a0\ufe0f ERROR HANDLER\nCatches any failure in the AI Agent (bad API key, rate limit, tool failure, etc.) and tells Alex instead of failing silently.\n\nSends: `ALTUS error: [error message]` back to the same Telegram chat.",
"height": 220,
"width": 300,
"color": 2
},
"id": "sticky-error",
"name": "Sticky Note - Error",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
660,
460
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "assign-error-text",
"name": "errorText",
"type": "string",
"value": "={{ (() => {\n const e = $json.error ?? $json.errorMessage ?? $json.cause ?? $json.description ?? $json.message ?? '';\n const msg = (typeof e === 'object' ? (e?.message ?? e?.error ?? JSON.stringify(e)) : String(e)).toLowerCase();\n if (msg.includes('credit') || msg.includes('quota') || msg.includes('billing') || msg.includes('insufficient')) {\n return 'ALTUS is temporarily unavailable \u2014 the AI provider has no available credits. Please check billing.';\n }\n if (msg.includes('invalid') && (msg.includes('key') || msg.includes('api') || msg.includes('auth') || msg.includes('credential'))) {\n return 'ALTUS error: AI provider credentials are invalid. Please check the API key configuration.';\n }\n if (msg.includes('rate limit') || msg.includes('too many request') || msg.includes('429')) {\n return 'ALTUS is rate-limited by the AI provider. Please try again in a moment.';\n }\n if (msg.includes('timeout') || msg.includes('timed out') || msg.includes('econnreset') || msg.includes('etimedout')) {\n return 'ALTUS timed out reaching the AI provider. Please try your request again.';\n }\n if (msg.includes('empty') || msg === '' || $json.normalizedResponse === '') {\n return 'ALTUS received an empty response from the AI. Please try your request again.';\n }\n if (msg.includes('tool') || msg.includes('function')) {\n return 'ALTUS encountered an issue with a connected tool. Please rephrase your request.';\n }\n return 'ALTUS encountered an unexpected error. Our team has been notified \u2014 please try again shortly.';\n})() }}"
},
{
"id": "assign-error-chatid",
"name": "chatId",
"type": "string",
"value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
}
]
},
"options": {}
},
"id": "set-error-message",
"name": "Format Error Message",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
660,
620
]
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "={{ \"ALTUS error: \" + ($json.error ? $json.error.message : \"Unknown error \u2014 check executions.\") }}",
"additionalFields": {
"parse_mode": "Markdown",
"appendAttribution": false
}
},
"id": "telegram-send-error",
"name": "Send Error to Telegram",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
880,
620
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## \ud83d\udea8 GLOBAL ERROR TRIGGER (optional)\nAttach this workflow as the \"Error Workflow\" in Settings for extra safety net, in addition to the inline error branch on the AI Agent node.\n\nSee SETUP_GUIDE.md step 8 for how to enable Error Workflow setting.",
"height": 180,
"width": 320,
"color": 2
},
"id": "sticky-error-trigger",
"name": "Sticky Note - Error Trigger",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-780,
460
]
},
{
"parameters": {
"url": "https://connect.squareup.com/v2/payments",
"method": "GET",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{SQUARE_TOKEN}}"
}
]
},
"description": "Search Square for payments and invoices. Use to check if a customer has paid or look up payment status.",
"descriptionType": "manual",
"toolDescription": "Search Square payments and invoices for Garage Force of West Michigan",
"options": {}
},
"id": "square-search-tool",
"name": "Square Search Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1200,
620
]
},
{
"parameters": {
"url": "https://connect.squareup.com/v2/customers/search",
"method": "POST",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer {{SQUARE_TOKEN}}"
}
]
},
"sendBody": true,
"description": "Search Square customer records by name or phone number",
"descriptionType": "manual",
"toolDescription": "Search Square customer records by name or phone number",
"options": {}
},
"id": "square-customer-tool",
"name": "Square Customer Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1340,
620
]
},
{
"parameters": {
"toolDescription": "Read and manage the Flores Crew Worker App (crew scheduling and task system). Base URL: https://gfwmcrew.pplx.app. Available endpoints: GET /api/workers (list crew), GET /api/schedules (list schedules), GET /api/schedules/active (today's active job), GET /api/schedules/:id (job details with tasks), POST /api/schedules (create schedule \u2014 body: {date, title, address, meetTime, meetLocation, notes}), POST /api/schedules/:id/tasks (add task), GET /api/templates (task templates), GET /api/inventory (inventory items). Always use x-app-token header: GFWM for boss access. Use this to check crew schedules, see today's active job, publish jobs to crew, and check task status.",
"method": "GET",
"url": "=https://gfwmcrew.pplx.app/api/{{ $fromAI('endpoint', 'API endpoint path e.g. workers, schedules, schedules/1/tasks', 'string') }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-app-token",
"value": "GFWM"
}
]
},
"options": {}
},
"id": "worker-app-tool",
"name": "Worker App Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1260,
760
]
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const binaryData = $input.item.binary;\nif (binaryData && binaryData.data) {\n binaryData.data.fileName = (binaryData.data.fileName || 'voice.oga').replace('.oga', '.ogg').replace('.opus', '.ogg');\n binaryData.data.fileExtension = 'ogg';\n binaryData.data.mimeType = 'audio/ogg';\n}\nreturn $input.item;"
},
"id": "rename-to-ogg",
"name": "Rename to .ogg",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-30,
320
]
},
{
"parameters": {
"toolDescription": "Search and read Garage Force leads, quotes, and jobs from Kronos CRM. Returns all 175 pipeline records with customer name, phone, email, address, square footage, quote amount, stage (Lead/Quote/Job), and last activity. Use this when Alex asks about open quotes, leads, pipeline status, customer follow-ups, who needs a call, or job status. Endpoint: GET https://kronoskairos.club/api/leads",
"method": "GET",
"url": "https://kronoskairos.club/api/leads",
"authentication": "none",
"options": {}
},
"id": "kronos-leads-tool",
"name": "Kronos Leads Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1120,
760
]
},
{
"parameters": {
"toolDescription": "Get Garage Force business metrics and KPIs from Kronos. Returns leads per week, quotes out, installs booked, open to-dos, check-in compliance, and pipeline summary stats. Use this when Alex asks for a business overview, weekly numbers, or how the pipeline is performing.",
"method": "GET",
"url": "https://kronoskairos.club/api/metrics",
"authentication": "none",
"options": {}
},
"id": "kronos-metrics-tool",
"name": "Kronos Metrics Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1120,
920
]
},
{
"parameters": {
"toolDescription": "Get scheduled events from Kronos calendar. Returns all 134 events including quotes, installs, meetings, and deadlines with customer name, address, phone, date, and event type. Use this when Alex asks what is scheduled, upcoming installs, or calendar events.",
"method": "GET",
"url": "https://kronoskairos.club/api/events",
"authentication": "none",
"options": {}
},
"id": "kronos-calendar-tool",
"name": "Kronos Calendar Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1260,
920
]
},
{
"parameters": {
"toolDescription": "Get open to-do items and tasks from Kronos. Returns all 51 to-dos with title, status (open/done), owner, and priority. Use when Alex asks what needs to be done, open tasks, or follow-up items.",
"method": "GET",
"url": "https://kronoskairos.club/api/todos",
"authentication": "none",
"options": {}
},
"id": "kronos-todos-tool",
"name": "Kronos Todos Tool",
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [
1400,
760
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "cond-allowlist",
"leftValue": "={{ $json.message.from.id }}",
"rightValue": "8268130574",
"operator": {
"type": "string",
"operation": "notEquals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "allow-list-check",
"name": "Allow-list Check",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-500,
60
]
},
{
"parameters": {
"chatId": "={{ $json.message.chat.id }}",
"text": "Unauthorized.",
"additionalFields": {
"parse_mode": "Markdown",
"appendAttribution": false
}
},
"id": "reject-unauthorized",
"name": "Reject Unauthorized",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-500,
220
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "v7 Changes: (1) Groq key moved to credential, (2) Telegram allow-list added, (3) Worker App auth fixed-closed",
"height": 200,
"width": 320,
"color": 3
},
"id": "sticky-v7-changes",
"name": "Sticky Note - v7 Changes",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-780,
-420
]
},
{
"parameters": {
"content": "## v8 Changes\n- \u2705 Estimate Tool \u2014 HTTP Request Tool (built-in)\n- \u2705 POST /estimate \u2192 Render API\n- \u2705 Generates branded PDF (logo, VeriFloor, warranty)\n- \u2705 n8n Gmail Tool sends email to customer\n- \u2705 All v7 fixes preserved\n\n**Render URL:** https://altus-estimate-api.onrender.com\n**API Key:** altus-gfwm-2026\n**Node type:** @n8n/n8n-nodes-langchain.toolHttpRequest v1.1",
"height": 220,
"width": 380,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1400,
1060
],
"id": "sticky-v8-changes",
"name": "Sticky Note - v8 Changes"
},
{
"parameters": {
"toolDescription": "Generate a Garage Force PDF estimate and email it to the customer. Use when Alex says 'make an estimate', 'create a quote', 'build estimate for [customer]'. Pass all customer and job details as a JSON body. The API generates the branded PDF and emails it automatically. Required fields: customer first_name, last_name, city, state, email; measurements manual_total_sqft; line_items array with description/quantity/rate. Default rate: 6.50/sqft. Default tax: 6%. Default deposit: 15%. Default terms: Net 15.",
"method": "POST",
"url": "https://altus-estimate-api.onrender.com/estimate",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-API-Key",
"value": "altus-gfwm-2026"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ estimate: { number: 'AUTO', date: $fromAI('estimate_date', 'YYYY-MM-DD, default today', 'string'), terms: 'Net 15', install_date: $fromAI('install_date', 'install date YYYY-MM-DD or TBD', 'string') }, customer: { first_name: $fromAI('first_name', 'customer first name', 'string'), last_name: $fromAI('last_nam
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
anthropicApigmailOAuth2googleCalendarOAuth2ApihttpHeaderAuthtelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
ALTUS v8 — Garage Force AI Command Center. Uses telegramTrigger, telegram, httpRequest, agent. Event-driven trigger; 42 nodes.
Source: https://gist.github.com/aflores-max/14a8a23668c361ef842b738636780d56 — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Telegram Trigger receives incoming messages (text, voice, photo, document). Switch routes by message type to appropriate processors: Text → forwarded as-is. Voice → downloaded and sent to Transcribe a
Transform your Telegram messenger into a powerful, multi-modal personal or team assistant. This n8n workflow creates an intelligent agent that can understand text, voice, images, and documents, and ta
Personal Assistant. Uses memoryBufferWindow, agent, agentTool, httpRequestTool. Event-driven trigger; 77 nodes.
Jarvis is a powerful multi-agent productivity assistant built in n8n. It works directly from Telegram and can understand both text messages and voice notes.
Inbox Guardian. Uses gmailTrigger, lmChatOpenAi, agent, textClassifier. Event-driven trigger; 66 nodes.