Telegram Bot for Plumber - Best Place to Learn or 1-1

I am trying to create a bot in telegram with the overarching aim for me (as a plumber) to be able to quote the customer, invoice the customer and manage deductions from my job (any parts used). I also want this to interact with Xero and track the jobs on a spreadsheet.

I am unsure if this is possible and I have read the guides, but I am struggling now. I have created a telegram bot via bot father, then I have added a telegram trigger to n8n, followed by Switch Node, followed by Set Initial Node and finally telegram “send message and wait for reponse”

I message the bot “invoice dave £200” and it responds with “Customer Email?” but when I enter the email, it loops back to “Customer Email?” endlessly.

At the moment I am only as far as this, so want to get the whole thing functioning properly, but for now cannot get past this points and I am very confused!

Unsure where I could learn to create this myself or if someone had time I would be willing to pay for some training so I get set this workflow up.

Thanks

Workflow: Telegram Trigger - Switch - Set Initial - telegram send message and wait for response.

Using Macbook Air

Hi @Rich

You’re definitely on the right track—and yes, what you’re aiming for is 100% possible in n8n with Telegram, Xero, and Google Sheets integration. The issue you’re facing with the loop is a common one when working with Telegram and n8n sessions. Here’s what’s likely going wrong and how to fix it:


Why the Bot Loops at “Customer Email?”

The Telegram node: Send Message and Wait for Response holds execution until it receives a response from the same chat, linked to that execution. However, the Telegram Trigger node will fire every time someone sends a new message, creating a new execution—not linked to the previous one.

So the “wait for response” node never gets that input—it’s stuck waiting.


Solution

You need to store the user’s progress/state using either a database, Google Sheet, or n8n’s built-in workflow state (via variables or external storage), and check what step the user is on before prompting the next question.


Simplified Flow (for quoting and invoicing)

  1. Telegram Trigger

  2. Switch Node — Checks if the message starts with invoice

  3. Set Node — Save customer name and amount from the message (e.g., invoice dave £200)

  4. Store State (e.g., in a Google Sheet or a database like Supabase or Notion):

User ID

Job ID

Current step (e.g., “waiting_email”)

Captured fields so far (name, amount, etc.)

  1. Respond with: “Customer Email?”

  2. Next time the bot gets a message, read their current state, see they’re at “waiting_email”, and proceed accordingly.

I hope this helps.

Thank you so much, I will try this and revert Miquel.

Do you mind if I keep this thread open as when I run through each stage I may need assistance and was struggling to find something specific for help

1 Like

So my new set up is telegram trigger - swtich - edit fields - Google Sheet (append row). Now when I ask my bot “invoice susan £200” the spreadsheet is updating, but not correctly. it is updating like this

User ID Job ID Step Name Amount Email
{{ $(“Set Initial”).json[“user_id”] }} {{ $(“Set Initial”).json[“job_id”] }} {{ $(“Set Initial”).json[“step”] }} {{ $(“Set Initial”).json[“name”] }} {{ $(“Set Initial”).json[“amount”] }}

I have checked executions and in the edit fields, it is not pulling through user ID either, ({{ $node[“Telegram Trigger”].json[“chat”][“id”] }}) it shows as null.

I am trying to make sure each step is correct before moving on to the response from telegram to say “customer email?”

Unsure if I can share what I have done so far or you are able to guide me through the steps as I still have a long way to go before getting this to the point of quoting, invoicing etc.

Paste your workflow in json format. You can copy from your n8n.

That’s the way to help you as much as possible.

Hi Miquel, thank you so much. Here it is, hopefully it makes more sense to you!

{
“nodes”: [
{
“parameters”: {
“updates”: [
“message”
],
“additionalFields”: {}
},
“type”: “n8n-nodes-base.telegramTrigger”,
“typeVersion”: 1.1,
“position”: [
-460,
-40
],
“id”: “d2b21554-b8dd-4614-8f66-1cb085e345b8”,
“name”: “Telegram Trigger”,
“webhookId”: “def7ee7f-dc31-40dc-b1aa-3a6c3ef76d78”,
“credentials”: {
“telegramApi”: {
“id”: “Y7k0xL2fodbrvjCN”,
“name”: “Telegram account”
}
}
},
{
“parameters”: {
“rules”: {
“values”: [
{
“conditions”: {
“options”: {
“caseSensitive”: true,
“leftValue”: “”,
“typeValidation”: “loose”,
“version”: 2
},
“conditions”: [
{
“leftValue”: “={{ $node["Telegram Trigger"].json["message"]["text"] }}\n\n”,
“rightValue”: “=Invoice”,
“operator”: {
“type”: “string”,
“operation”: “contains”
},
“id”: “b610fd96-6046-4489-afc4-6e0244d5bba6”
}
],
“combinator”: “and”
},
“renameOutput”: true,
“outputKey”: “1”
}
]
},
“looseTypeValidation”: true,
“options”: {}
},
“type”: “n8n-nodes-base.switch”,
“typeVersion”: 3.2,
“position”: [
-240,
-40
],
“id”: “0d99ff4f-868e-4f80-b506-397dbe1cae43”,
“name”: “Switch”,
“alwaysOutputData”: true
},
{
“parameters”: {
“operation”: “append”,
“documentId”: {
“__rl”: true,
“value”: “https://docs.google.com/spreadsheets/d/1oab8-G4UfLzgzelzlkJWLUTMorMFksMzn_0nZf5a3wA/edit”,
“mode”: “url”
},
“sheetName”: {
“__rl”: true,
“value”: “State”,
“mode”: “name”
},
“columns”: {
“mappingMode”: “defineBelow”,
“value”: {
“User ID”: “{{ $("Set Initial").json["user_id"] }}”,
“Job ID”: “{{ $("Set Initial").json["job_id"] }}”,
“Step”: “{{ $("Set Initial").json["step"] }}”,
“Name”: “{{ $("Set Initial").json["name"] }}”,
“Amount”: “{{ $("Set Initial").json["amount"] }}”
},
“matchingColumns”: ,
“schema”: [
{
“id”: “User ID”,
“displayName”: “User ID”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “Job ID”,
“displayName”: “Job ID”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “Step”,
“displayName”: “Step”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “Name”,
“displayName”: “Name”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “Amount”,
“displayName”: “Amount”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
},
{
“id”: “Email”,
“displayName”: “Email”,
“required”: false,
“defaultMatch”: false,
“display”: true,
“type”: “string”,
“canBeUsedToMatch”: true,
“removed”: false
}
],
“attemptToConvertTypes”: false,
“convertFieldsToString”: false
},
“options”: {}
},
“type”: “n8n-nodes-base.googleSheets”,
“typeVersion”: 4.5,
“position”: [
420,
-40
],
“id”: “90cc4be2-f280-425f-b734-2eb2acef920a”,
“name”: “Save State”,
“credentials”: {
“googleSheetsOAuth2Api”: {
“id”: “Ute04etYCFbArbJ7”,
“name”: “Google Sheets account”
}
}
},
{
“parameters”: {
“assignments”: {
“assignments”: [
{
“id”: “43c7e452-1fa1-4267-b825-29f2bc7266b2”,
“name”: “user_id”,
“value”: “={{ $node["Telegram Trigger"].json["chat"]["id"] }}”,
“type”: “string”
},
{
“id”: “59f92845-9bd5-4e56-aa91-91f073ce4692”,
“name”: “job_id”,
“value”: “={{ Date.now() }}”,
“type”: “string”
},
{
“id”: “b53948ce-6e4a-41d4-b3cf-f2230578a7b1”,
“name”: “step”,
“value”: “waiting_email”,
“type”: “string”
},
{
“id”: “c9ee1ba9-ad1f-4e09-a7a2-a79a23023952”,
“name”: “name”,
“value”: “={{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[1] }}”,
“type”: “string”
},
{
“id”: “10cd5213-ebe9-4d2c-a95b-7c9e2bf3abc0”,
“name”: “amount”,
“value”: “={{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[2] }}”,
“type”: “string”
}
]
},
“options”: {}
},
“type”: “n8n-nodes-base.set”,
“typeVersion”: 3.4,
“position”: [
-20,
-40
],
“id”: “2077163e-5bc6-461b-bfd8-693938bb18bc”,
“name”: “Set Initial”
}
],
“connections”: {
“Telegram Trigger”: {
“main”: [
[
{
“node”: “Switch”,
“type”: “main”,
“index”: 0
}
]
]
},
“Switch”: {
“main”: [
[
{
“node”: “Set Initial”,
“type”: “main”,
“index”: 0
}
]
]
},
“Set Initial”: {
“main”: [
[
{
“node”: “Save State”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“templateCredsSetupCompleted”: true,
“instanceId”: “702b787e48994cf9fb70bdb3408d7efb00601b2a7ae1fab75f4c4c7a2a703675”
}
}

Hi Miquel, whenever you can review and advise that would be great. Additionally, if you can advise I am on the right track for my overarching goal and any tips to make this work it would be appreciated.

I have some staff I would like to use this as part of my business so they can quote, invoice, and track everything on sheets and with xero incorporating any costs associated per job.

The idea to make this seamless and fast but also accurate. I have signed up to PDF money and stripe to insert a payment link in the pdf. so want to make all of these sing together!

Thanks for the update — that really helps. You’re very close, and the issue you’re seeing now is common when variables aren’t getting pulled through properly.

What’s going wrong

Right now your spreadsheet is filling in the text:

{{ $("Set Initial").json["user_id"] }}

Instead of inserting the actual value (like 123456789), it’s writing the placeholder as-is. That means n8n isn’t replacing the variable because it doesn’t understand where to get the data.

Also, the user ID is showing up as null in executions — which tells us the Set node may not be getting the Telegram data properly.


Here’s how to fix it

1. Make sure the Set node is correctly connected

In your Set node, check these fields:

user_id = {{ $node["Telegram Trigger"].json["chat"]["id"] }}
name = {{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[1] }}
amount = {{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[2] }}

Make sure you don’t have smart quotes like “ ” — they need to be normal ones: " "
If you’re copy-pasting from Telegram or a doc, that sometimes sneaks in.

2. In the Google Sheets node

Instead of:

{{ $("Set Initial").json["user_id"] }}

Use:

{{ $json["user_id"] }}

Why? Because the Google Sheets node is running right after the Set node — so the current $json already includes all the values you just set. You don’t need to reference the node name again.

So your field mappings should look like:

User ID: {{ $json["user_id"] }}
Job ID:  {{ $json["job_id"] }}
Step:    {{ $json["step"] }}
Name:    {{ $json["name"] }}
Amount:  {{ $json["amount"] }}

Bonus tip – test output

If you’re not sure what’s getting set, click on the Set node, go to Executions → Output, and check the values. You should see things like:

{
  "user_id": "12345678",
  "name": "Susan",
  "amount": "ÂŁ200",
  ...
}

If it’s empty or shows null, then the message format might be off — maybe you typed invoice susan (extra space), or there’s a typo. Let me know and I can help make the parsing more flexible too.

Thanks So much again Miquel for helping me, but I am still having an issue, even though I am typing and copying straight from your posts (tried both).

Goal:

Text “Invoice Susan ÂŁ200” → Bot asks “Customer email?” once → Stores “Susan,” “£200,” “[email protected]” in Google Sheet. Then I want to add a further description i.e. what work was done for Susan, test the invoice sent and payment link, then record in excel costs of job via the bot and link to xero.

Current Flow / Stage:

  • “Telegram Trigger” → “Switch” (checks “Invoice”) → “Set Initial” (Edit Fields) → “Save State” (Google Sheets Append Row).

Set Initial Settings:

  • user_id: {{ $node["Telegram Trigger"].json["chat"]["id"] }}
  • job_id: {{ Date.now() }}
  • step: waiting_email (plain text)
  • name: {{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[1] }}
  • amount: {{ $node["Telegram Trigger"].json["message"]["text"].split(" ")[2] }}

Save State Settings:

  • Columns: user_id, job_id, step, name, amount, email
  • Values:
    • User ID: {{ $json["user_id"] }}
    • Job ID: {{ $json["job_id"] }}
    • Step: {{ $json["step"] }}
    • Name: {{ $json["name"] }}
    • Amount: {{ $json["amount"] }}
    • Email: (blank)

Test: “Invoice Susan £200”
Set Initial Output:
[
{
“user_id”: “\n\n”,
“job_id”: “1742900251253\n\n”,
“step”: “waiting_email”,
“name”: “susan\n\n”,
“amount”: “£200\n\n”
}
]

Sheet Row:

Same raw values with newlines, not “993616324,” “Susan,” etc.

Problem:

  • “user_id” is “\n\n” not “993616324” (chat ID from Trigger is “993616324”).
  • Newlines (“\n”) everywhere—values not resolving clean.

Trigger Output:
[
{
“chat”: {“id”: “993616324”},
“text”: “Invoice susan £200”
}
]

Don’t know if this assists further or how to resolve! Sorry for taking up your time, just want to get this working!

Thanks!

Just to let you know, I have restarted with an agent as I found this too difficult to incorporate all my ideas. I will revert on this thread with any questions if thats ok

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.