AutomationFlowsAI & RAG › Altus V8 — Garage Force AI Command Center (telegram Trigger)

Altus V8 — Garage Force AI Command Center (telegram Trigger)

ALTUS v8 — Garage Force AI Command Center. Uses telegramTrigger, telegram, httpRequest, agent. Event-driven trigger; 39 nodes.

Event trigger★★★★★ complexityAI-powered39 nodesTelegram TriggerTelegramHTTP RequestAgentMemory Buffer WindowGmail ToolGoogle Calendar ToolHTTP Request Tool
AI & RAG Trigger: Event Nodes: 39 Complexity: ★★★★★ AI nodes: yes Added:
Altus V8 — Garage Force AI Command Center (telegram Trigger) — n8n workflow card showing Telegram Trigger, Telegram, HTTP Request integration

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 →

Download .json
{
  "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": "4f6d426f-0ec2-4a55-9ca8-a22dd2fd13df",
      "name": "Sticky Note - Input",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -160,
        352
      ]
    },
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "id": "552d7c71-945d-44bc-97a0-e35ce260863c",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.1,
      "position": [
        192,
        896
      ],
      "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": "f4e4825c-d373-488f-9076-d42166d92c87",
      "name": "Sticky Note - Router",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        544,
        288
      ]
    },
    {
      "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": "e34ac8f1-e93b-4053-8a08-d2fc8ccaa640",
      "name": "Message Type Router",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        640,
        800
      ]
    },
    {
      "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": "e29cf09c-d074-4a89-be2d-0bff8f0c302c",
      "name": "Sticky Note - Voice",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        592,
        1216
      ]
    },
    {
      "parameters": {
        "resource": "file",
        "fileId": "={{ $json.message.voice.file_id }}",
        "additionalFields": {}
      },
      "id": "b85606ae-312e-447e-be88-2bd8ba1aa9bd",
      "name": "Get Voice File",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        864,
        896
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/audio/transcriptions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "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": {}
          }
        }
      },
      "id": "36067a58-1c66-461c-90a3-28f0d045c070",
      "name": "Groq Voice Transcription",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1312,
        896
      ],
      "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 }}"
            },
            {
              "id": "ee44f542-8bbf-4d81-bd6e-b798ae4e2fc8",
              "name": "",
              "value": "",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "f3d05c70-2924-406e-a69d-59260a91bf27",
      "name": "Prepare Voice Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1536,
        896
      ]
    },
    {
      "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": "244803fd-88a9-4991-83a2-d612bcf03acf",
      "name": "Prepare Text Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1536,
        704
      ]
    },
    {
      "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": "73919f8c-e489-43c0-8de6-7f52bb798ec0",
      "name": "Sticky Note - Agent",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1312,
        304
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.chatInput }}",
        "options": {
          "systemMessage": "=SYSTEM PROMPT \u2014 ALTUS (Layer 1: Static Doctrine)\nTelegram/n8n command center for Garage Force of West Michigan (FLORES CREW).\n\n====================================================\n1. IDENTITY\n====================================================\nYou are ALTUS, embedded AI operator for Alex Flores-Calderon's concrete\nfloor coating business. You talk to Alex over Telegram. Give decisions\nand drafts, not data dumps. Treat messages as from Alex unless clearly\nforwarded from a customer or crew member.\n\nBusiness facts (pricing, job board, crew, customers, product specs)\nare injected via {businessContext}. Never use stale facts; never\ninvent facts absent from {businessContext} or tool results.\n\n====================================================\n2. HARD RULES (NEVER BREAK)\n====================================================\n1. NO PRICES EVER to workers \u2014 no revenue, pricing, or pay in anything\n   that could reach a crew member (schedules, job sheets, group texts).\n2. ALWAYS show a draft before sending any email, text, or invoice.\n   Never auto-send.\n3. ALWAYS confirm the customer/third-party email or phone on file\n   before sending to them. If unsure, ask.\n4. NEVER fabricate customer data, job numbers, dates, or figures. If\n   missing/unclear, say so and ask.\n5. Keep Tree of Life (ToL) and Garage Force (GF) finances strictly\n   separate in every calc and recommendation. Flag any cross-entity\n   transfer explicitly.\n6. Apply the 400 sqft minimum job size; flag jobs under 350 sqft as\n   likely unprofitable before quoting or scheduling.\n\n====================================================\n3. CONFIRMATION RULES\n====================================================\nACT IMMEDIATELY: internal math (materials, payroll, tax/royalty/\ndistribution, profitability); info lookups; drafting for review\n(drafting is not sending).\n\nCONFIRM FIRST: sending anything external; money leaving an account,\ndeposits, or ToL/GF transfers; schedule changes affecting the crew's\nday; off-default pricing; missing/unclear contact info; any estimate\nunder the 400 sqft minimum.\n\n====================================================\n4. TOOL USAGE RULES\n====================================================\n- Use tools silently. No \"checking\", \"let me look\", or \"I'll\".\n  Call the tool, then answer with the result.\n- Never invent a tool result. If a tool fails, say what is missing.\n- Estimate requests: call estimate_tool immediately. Ask only if\n  name or sqft is completely missing.\n- Outbound emails: HTML only with signature block.\n\n====================================================\n5. RESPONSE CONTRACT \u2014 NON-NEGOTIABLE\n====================================================\nOutput a single JSON object only, no markdown fences:\n{\n  \"reply\": \"<concise Telegram message, plain text only>\",\n  \"action_taken\": \"<what was completed, or null>\",\n  \"needs_approval\": false,\n  \"approval_request\": null,\n  \"error\": null\n}\n\nREPLY FIELD RULES:\n- Answer the exact question first. Do not repeat the question.\n- No narrating reasoning or tool calls.\n- Simple questions: 1-3 sentences, max 300 chars.\n- Completed actions: state what was done and the key result only.\n- Lists only when 3 or more items genuinely need enumeration.\n- No unsolicited advice unless it prevents a serious mistake.\n- At most one clarifying question at a time.\n- Choose confidently. Do not present options you could decide.\n- The move: allowed but not mandatory. Use only when a next step adds value.\n- Never invent tool results or figures.\n- Length: SIMPLE up to 300 chars, NORMAL up to 800, DETAILED up to 2000 chars only if explicitly requested.\n- Never include raw errors, API payloads, credentials, worker pricing, or customer PII beyond what was asked."
        }
      },
      "id": "87ff76b2-0b8d-4c71-af3b-45e357a719b3",
      "name": "ALTUS AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        1488,
        1280
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "contextWindowLength": 20
      },
      "id": "307c3998-8b30-49e4-b0b3-1ae946b23ec9",
      "name": "Window Buffer Memory (20 msgs)",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        1904,
        1024
      ]
    },
    {
      "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": "598cd21d-bfa5-4684-a08a-bc8e6e86c000",
      "name": "Sticky Note - Tools",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2368,
        704
      ]
    },
    {
      "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.",
        "operation": "getAll",
        "limit": 10,
        "filters": {
          "q": "={{ $fromAI('search_query', 'Gmail search query, e.g. from:someone or subject:invoice', 'string') }}"
        }
      },
      "id": "bfd6a0ac-3432-474c-9512-efac51ca71b9",
      "name": "Gmail Tool",
      "type": "n8n-nodes-base.gmailTool",
      "typeVersion": 2.1,
      "position": [
        2032,
        1024
      ],
      "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>",
        "sendTo": "={{ $fromAI('to', 'Recipient email address', 'string') }}",
        "subject": "={{ $fromAI('subject', 'Email subject line', 'string') }}",
        "message": "={{ $fromAI('body', 'Email body content', 'string') }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "22729585-582b-4f19-96ba-fe3011dd9079",
      "name": "Gmail Send Tool",
      "type": "n8n-nodes-base.gmailTool",
      "typeVersion": 2.1,
      "position": [
        2160,
        1024
      ],
      "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"
        },
        "limit": 20,
        "options": {}
      },
      "id": "049d6830-2486-4769-9a7a-d88c350843e2",
      "name": "Google Calendar Tool (Read)",
      "type": "n8n-nodes-base.googleCalendarTool",
      "typeVersion": 1.3,
      "position": [
        2288,
        1024
      ],
      "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.",
        "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": {
          "description": "={{ $fromAI('description', 'Event description', 'string') }}",
          "summary": "={{ $fromAI('title', 'Event title', 'string') }}"
        }
      },
      "id": "f9f71532-08de-4459-b217-3d57ea1be4b1",
      "name": "Google Calendar Tool (Create)",
      "type": "n8n-nodes-base.googleCalendarTool",
      "typeVersion": 1.3,
      "position": [
        2416,
        1024
      ],
      "credentials": {
        "googleCalendarOAuth2Api": {
          "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": "68e518cf-03ac-496b-9d83-ab15cd3bf22f",
      "name": "Sticky Note - Output",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2064,
        192
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message?.chat?.id ?? $('Telegram Trigger').item.json.message?.from?.id }}",
        "text": "={{ (() => { const raw = $json.output ?? ''; const clean = raw.replace(/^```json\\s*/,'').replace(/\\s*```$/,'').trim(); try { return JSON.parse(clean).reply ?? clean; } catch(e) { return clean; } })() }}",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "id": "ab5f0c47-2abf-4dc0-a159-710978d75dae",
      "name": "Send Response",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3632,
        704
      ],
      "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": "615bc4fb-afcc-4e62-8817-d23087498b37",
      "name": "Sticky Note - Error",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1280,
        1392
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "assign-error-text",
              "name": "errorText",
              "type": "string",
              "value": "=={{ (() => { const e = $json.error ?? $json.errorMessage ?? $json.cause ?? $json.description ?? ''; const msg = (typeof e === 'object' ? (e?.message ?? JSON.stringify(e)) : String(e)).toLowerCase(); if (msg.includes('credit') || msg.includes('quota') || msg.includes('billing')) { return 'ALTUS is temporarily unavailable \u2014 the AI provider has no available credits. Check billing at platform.openai.com or switch providers.'; } if (msg.includes('invalid') && (msg.includes('key') || msg.includes('auth'))) { return 'ALTUS error: AI provider credentials are invalid. Check the API key.'; } if (msg.includes('rate limit') || msg.includes('429')) { return 'ALTUS is rate-limited. Please try again in a moment.'; } if (msg.includes('timeout')) { return 'ALTUS timed out. Please try again.'; } return 'ALTUS encountered an unexpected error. Please try again shortly.'; })() }}"
            },
            {
              "id": "assign-error-chatid",
              "name": "chatId",
              "type": "string",
              "value": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
            }
          ]
        },
        "options": {}
      },
      "id": "4f64ab26-ff77-42ea-8ce4-d86c3e0c8da6",
      "name": "Format Error Message",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3632,
        896
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
        "text": "={{ \"ALTUS error: \" + ($json.error ? $json.error.message : \"Unknown error \u2014 check executions.\") }}",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "id": "f320575a-d79f-4462-bca6-2a900fad2b57",
      "name": "Send Error to Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        3856,
        896
      ],
      "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": "e56dbbdc-c93e-42b5-aa08-da7e404a9e91",
      "name": "Sticky Note - Error Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        32,
        1072
      ]
    },
    {
      "parameters": {
        "toolDescription": "Search Square payments and invoices for Garage Force of West Michigan",
        "url": "https://connect.squareup.com/v2/payments",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{SQUARE_TOKEN}}"
            }
          ]
        },
        "options": {}
      },
      "id": "f114ccef-127d-43cc-968b-5c05f84e4538",
      "name": "Square Search Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        2672,
        1024
      ]
    },
    {
      "parameters": {
        "toolDescription": "Search Square customer records by name or phone number",
        "method": "POST",
        "url": "https://connect.squareup.com/v2/customers/search",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{SQUARE_TOKEN}}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "options": {}
      },
      "id": "d482a7ee-a5f6-44a8-89fb-00744126916d",
      "name": "Square Customer Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        2784,
        1168
      ]
    },
    {
      "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.",
        "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": "30efcdb3-4798-4ac7-bd85-b3fd13cc2ae1",
      "name": "Worker App Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        2928,
        1280
      ]
    },
    {
      "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": "fa079b7f-1fc8-40fb-9f3b-ee71fad4d376",
      "name": "Rename to .ogg",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1088,
        896
      ]
    },
    {
      "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",
        "url": "https://kronoskairos.club/api/leads",
        "options": {}
      },
      "id": "82f4be72-b619-4ef6-8a8a-15364e3de108",
      "name": "Kronos Leads Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        3056,
        1392
      ]
    },
    {
      "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.",
        "url": "https://kronoskairos.club/api/metrics",
        "options": {}
      },
      "id": "abee6477-96f8-47ed-94df-c49bca3ace4a",
      "name": "Kronos Metrics Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        3216,
        1456
      ]
    },
    {
      "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.",
        "url": "https://kronoskairos.club/api/events",
        "options": {}
      },
      "id": "8a907ae6-67c2-42f9-a1ee-30834b9aaac2",
      "name": "Kronos Calendar Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        3360,
        1536
      ]
    },
    {
      "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.",
        "url": "https://kronoskairos.club/api/todos",
        "options": {}
      },
      "id": "0dc63e8a-e776-47dc-b4d4-df1a3443dae5",
      "name": "Kronos Todos Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        3504,
        1648
      ]
    },
    {
      "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": "f97b7977-4aa9-4fbd-9e9a-dbca7220c148",
      "name": "Allow-list Check",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        416,
        896
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $json.message.chat.id }}",
        "text": "Unauthorized.",
        "additionalFields": {
          "appendAttribution": false,
          "parse_mode": "Markdown"
        }
      },
      "id": "9c2e2271-cd57-4542-b314-e717ef8f92ac",
      "name": "Reject Unauthorized",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        640,
        992
      ],
      "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": "51744c88-8768-4549-b7b8-3a6e58f9bb60",
      "name": "Sticky Note - v7 Changes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "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": [
        2224,
        1680
      ],
      "id": "0707dd74-e45b-4dec-8479-d9312f24042c",
      "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",
        "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_name', 'customer last name', 'string'), address: $fromAI('address', 'street address or empty', 'string'), city: $fromAI('city', 'city', 'string'), state: $fromAI('state', 'state abbreviation', 'string'), zip: $fromAI('zip', 'zip code or empty', 'string'), phone: $fromAI('phone', 'phone or empty', 'string'), email: $fromAI('email', 'customer email', 'string') }, measurements: { manual_total_sqft: $fromAI('sqft', 'total square footage as number', 'number') }, line_items: [{ description: $fromAI('coating', '1/4 inch Full Chip Floor Coating', 'string'), quantity: $fromAI('sqft2', 'square footage', 'number'), rate: $fromAI('rate', '6.50', 'number') }], tax: { rate_percent: 6, taxable_percent: 100 }, deposit_percent: 15, flake_color: $fromAI('flake_color', 'UNDECIDED', 'string'), scope_note: $fromAI('scope_note', 'Full floor coating system start to finish.', 'string') }) }}",
        "options": {}
      },
      "id": "0bfbbd28-3872-488b-973e-f0b945291384",
      "name": "Estimate Tool",
      "type": "n8n-nodes-base.httpRequestTool",
      "typeVersion": 4.2,
      "position": [
        208,
        1440
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/audio/speech",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\"model\":\"canopylabs/orpheus-v1-english\",\"input\":\"={{ $('ALTUS AI Agent').item.json.output }}\",\"voice\":\"daniel\",\"response_format\":\"mp3\"}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        192,
        464
      ],
      "id": "e767e802-8c34-4382-afe4-e0e94a0edcd2",
      "name": "Groq TTS",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.telegram.org/bot8679996365:AAG6F2mEcNlOdVbA1iNGHbOFDNw8TSToYQE/sendVoice",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "=={{ $('Telegram Trigger').item.json.message.chat.id }}"
            },
            {
              "parameterType": "formBinaryData",
              "name": "voice",
              "inputDataFieldName": "data"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        416,
        464
      ],
      "id": "c652c321-68cc-4813-93ba-f009377f9d88",
      "name": "Send Voice Response"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "=claude-sonnet-4-5-20250929",
          "mode": "id"
        },
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.5,
      "position": [
        1776,
        1024
      ],
      "id": "7971bb7e-5da0-4001-b6de-fccb97460914",
      "name": "Anthropic Chat Model",
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Allow-list Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message Type Router": {
      "main": [
        [
          {
            "node": "Get Voice File",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Text Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Voice File": {
      "main": [
        [
          {
            "node": "Rename to .ogg",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Groq Voice Transcription": {
      "main": [
        [
          {
            "node": "Prepare Voice Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Voice Input": {
      "main": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Text Input": {
      "main": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Window Buffer Memory (20 msgs)": {
      "ai_memory": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Send Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Google Calendar Tool (Read)": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "ALTUS AI Agent": {
      "main": [
        [
          {
            "node": "Send Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Error Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Error Message": {
      "main": [
        [
          {
            "node": "Send Error to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Square Search Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Square Customer Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Rename to .ogg": {
      "main": [
        [
          {
            "node": "Groq Voice Transcription",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Worker App Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Kronos Leads Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Kronos Metrics Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Kronos Calendar Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Kronos Todos Tool": {
      "ai_tool": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Allow-list Check": {
      "main": [
        [
          {
            "node": "Reject Unauthorized",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Message Type Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Groq TTS": {
      "main": [
        [
          {
            "node": "Send Voice Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anthropic Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "ALTUS AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "e2dddb83-e2e2-4b97-ab96-29d8a45eeb1a",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodeGroups": [],
  "id": "gouACmtb6c1LvSwO",
  "tags": [
    {
      "updatedAt": "2026-07-29T00:24:09.683Z",
      "createdAt": "2026-07-29T00:24:09.683Z",
      "id": "MnNgOzmsWBvPamWO",
      "name": "Garage Force"
    },
    {
      "updatedAt": "2026-07-29T00:24:09.622Z",
      "createdAt": "2026-07-29T00:24:09.622Z",
      "id": "SGSLRyrdbgqv92mu",
      "name": "ALTUS"
    },
    {
      "updatedAt": "2026-07-29T00:24:09.653Z",
      "createdAt": "2026-07-29T00:24:09.653Z",
      "id": "zxLSC20XTaScG1TL",
      "name": "AI Assistant"
    }
  ]
}

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.

Pro

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; 39 nodes.

Source: https://gist.github.com/aflores-max/14a8a23668c361ef842b738636780d56 — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

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

Memory Buffer Window, Telegram Trigger, Telegram +12
AI & RAG

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

Memory Buffer Window, Telegram Trigger, Telegram +10
AI & RAG

Personal Assistant. Uses memoryBufferWindow, agent, agentTool, httpRequestTool. Event-driven trigger; 77 nodes.

Memory Buffer Window, Agent, Agent Tool +15
AI & RAG

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.

Telegram Trigger, Telegram, OpenAI +10
AI & RAG

Inbox Guardian. Uses gmailTrigger, lmChatOpenAi, agent, textClassifier. Event-driven trigger; 66 nodes.

Gmail Trigger, OpenAI Chat, Agent +12