Step-by-step installation guide Β· v1.2 Β· October 2026

Build Your Own Telegram Receipt & Claim Bot (No Coding)

Snap a receipt in Telegram. It lands in Google Sheets, the photo is filed in Google Drive, and your monthly claim form is emailed with one tap.

πŸ“· Receipt in Telegramβ†’πŸ€– AI reads itβ†’πŸ“Š Google Sheetβ†’πŸ“ Drive folderβ†’πŸ“§ /submit email

Target set-up time: typically under an hour.

For freelancers, small-company staff and the bosses who approve their claims. Malaysia-friendly examples (RM), usable anywhere.

Get the code β†’

Rangka AI Β· rangkaai.my Β· free guide

Contents

  1. What the bot does, and who it's for
  2. What you need (and what it costs)
  3. The plan (typically under an hour)
  4. Step 1: Create your Telegram bot (5 min)
  5. Step 2: Get your OpenAI key (5 min)
  6. Step 3: Paste the code and fill in the settings (10 min)
  7. Step 4: Run setup and allow Google permissions (5 min)
  8. Step 5: Make yourself admin and test it (5 min)
  9. Daily use: commands and tips
  10. Customising categories, limits and mileage
  11. Adding team members
  12. Privacy and security
  13. Troubleshooting FAQ
  14. Updating the bot and keeping it running
  15. Final checklist

This free guide does not include the code itself. Things change. Prices, button names and screens at Google, Telegram and OpenAI can change after this guide was written (October 2026). If a button has moved, look for a similar name nearby. The steps stay the same.

1. What the bot does, and who it's for

Most people keep claim receipts in a wallet or a drawer, then spend an evening at month end typing them into a form. This bot does that job as you go. You send it a receipt from your phone, and that's it.

You send the bot any of these:

The bot then:

  1. reads the date, shop, amount and payment method, and picks a category;
  2. adds a row to your own Google Sheet;
  3. saves the receipt in your own Google Drive, sorted as Year / Person / Month, with a clear file name like 2026-10-14_Fuel_Petronas_RM50.00.jpg;
  4. replies with what it saved, plus an Undo button in case it got something wrong.

At month end you type /generate to get an Excel claim form, or /submit to email the form, with every receipt attached, to your boss or accounts. The bot asks you to confirm before it sends anything.

How the pieces fit together
πŸ“± Telegram
you send a receipt
β†’
βš™οΈ Google Apps Script
the bot's "brain"; checks Telegram every minute
β†’
🧠 OpenAI
reads the receipt / voice note
πŸ“Š Google Sheet
one row per claim
πŸ“ Google Drive
receipt files, private
πŸ“§ Gmail
/submit emails the claim form

Everything runs in your Google account. There is no server to rent and no app to install. You only pay OpenAI a few sen per receipt.

Who it's for

What it is not

2. What you need (and what it costs)

You need Notes Cost
A Google account A normal free Gmail account works. Google Workspace works too. Free
Telegram on your phone Plus Telegram Desktop or web.telegram.org on your computer to make copying easier. Free
An OpenAI API account This is separate from a ChatGPT subscription; ChatGPT Plus does not include API credit. You add a small prepaid credit. Pay as you go
A computer with Chrome, Edge or Safari Setup is done on a computer. Daily use is on your phone. n/a
The Code.gs file One file, copy-paste only. This free guide explains every step; the ready-to-paste code is sold separately: get the code. see below
About 1 hour set aside A target, not a promise: the steps below typically take 30 to 40 minutes, and the rest is buffer for permission screens and typos. n/a

How much does OpenAI cost per receipt?

Honest numbers, worked out from OpenAI's published prices as of 17 October 2026 for the default models in the code (gpt-4o-mini for reading, whisper-1 for voice):

What you send Approximate cost each
Receipt photo about US$0.004 (around 2 sen)
Voice note (15 seconds) about US$0.002
Typed claim ("parking RM5") under US$0.0002
PDF invoice (1 page) usually similar to a photo or less; depends on the file

So 100 receipt photos a month comes to roughly US$0.40. A team of five sending 300 receipts a month would be around US$1.20 to 1.50.

These are estimates. Your real cost depends on photo size and on OpenAI's prices at the time. The bot records every request and its estimated cost in the TOKEN_LOG tab of your Sheet, messages the admin if one day's spend passes US$0.50, and has a daily spending cap (default US$1.00): before each AI call it checks that the call fits under the cap; if not, receipt photos and voice notes pause until the next day, while simple typed claims still work. You can change both numbers. These are the bot's own estimates, so also set a monthly budget limit in your OpenAI account (Step 2). Check current prices at openai.com/api/pricing or platform.openai.com/docs/pricing.

Model retirement notice. OpenAI has announced that gpt-4o-mini will be retired on 14 January 2027 and whisper-1 on 26 February 2027. When that happens, you change one or two words in the settings (see Section 14: Updating). The setup step also checks this and warns you if your model has a shutdown date.

You have everything except the code

This guide is free. The bot itself is one ready-to-paste file, Code.gs, sold with a PDF copy of this guide. You need it for Step 3.

Get the code β†’

3. The plan (typically under an hour)

Step What you do Time
1 Create the Telegram bot with @BotFather and copy its token 5 min
2 Create an OpenAI API key and add a little credit 5 min
3 Make a Google Sheet, paste Code.gs, fill in the settings, save the token and key 10 min
4 Run setup and click through Google's permission screens 5 min
5 Make yourself admin, register, and send a test receipt 5 min
Total hands-on time (target) typically about 30 min

Before you begin: open a notepad (Notes, Notepad, Google Keep) to hold your token and key for a few minutes only. Delete the note when you finish Step 3.

Golden rule: your Telegram token and OpenAI key are like passwords. Never paste them into a chat, an email, a screenshot, or into the code itself. In Step 3 they go into a protected place called Script Properties.

4. Step 1: Create your Telegram bot (5 min)

Telegram has an official bot called @BotFather that creates bots for you. It's free.

  1. In Telegram, search for @BotFather. Pick the one with the blue verified tick, then tap Start.
  2. Send /newbot.
  3. BotFather asks for a name. This is the display name people see, for example My Company Claims.
  4. Next it asks for a username. It must be unique and end in bot, for example mycompany_claims_bot. If the name is taken, try another.
  5. BotFather replies with a message containing your token. It looks like this made-up example:
What the token looks like (example only, not a real token)
123456789:AAH-Exampl3TokenDoNotUse_abcdefghijk
β¬… a number, a colon, then a long mix of letters and numbers. Copy the whole thing.
  1. Copy the token into your temporary note.
What you should see:BotFather chat showing /newbot, the name and username questions, and the reply with the token blurred out

Optional, but nice:

Lost or leaked the token? In @BotFather send /revoke, choose your bot, and you get a new token. The old one stops working at once. Put the new token in Script Properties (Step 3) and run setup again.

5. Step 2: Get your OpenAI key (5 min)

  1. Go to platform.openai.com and sign up or log in. You can use the same login as ChatGPT, but API billing is separate.
  2. Open Settings β†’ Billing and add a payment method plus a small prepaid credit. US$5 lasts most small users many months; see the costs in Section 2.
  3. Recommended: under Settings β†’ Limits (or your project's limits), set a monthly budget or alert so you can never be surprised.
  4. Open API keys and click Create new secret key. Name it Claim Bot.
  5. Copy the key now. OpenAI shows it only once. It starts with sk-. Paste it into your temporary note.
What you should see:OpenAI dashboard, "Create new secret key" dialog, with the key blurred out

Use a new key just for this bot. Don't reuse a key from another app. Then, if you ever stop using the bot, you can delete this one key without breaking anything else.

6. Step 3: Paste the code and fill in the settings (10 min)

3a. Create the Google Sheet

  1. On your computer, go to sheets.new (or Google Drive β†’ New β†’ Google Sheets).
  2. Name it, for example, Claim Bot Data. This Sheet becomes your bot's database.

3b. Open Apps Script and paste the code

  1. In the Sheet menu, click Extensions β†’ Apps Script. A new tab opens with a code editor and a file called Code.gs.
  2. At the top left, click Untitled project and rename it Claim Bot.
  3. Click inside the editor, select everything (Ctrl+A, or Cmd+A on a Mac) and delete it. The editor should be completely empty.
  4. Open Code.gs from the code package you bought in a text editor (Notepad, TextEdit, or VS Code), select all, copy, and paste it into the empty editor.
  5. Click the πŸ’Ύ Save icon (or Ctrl+S).
What you should see:Apps Script editor with the pasted code, the project renamed "Claim Bot", and the Save icon highlighted

3c. Fill in the settings (the CONFIG block)

The top of the code has a block called CONFIG. That's the only part you edit. Change the values between the quote marks and leave the quotes, commas and colons alone.

The CONFIG block: what to change
COMPANY_NAME: "My Company Sdn Bhd",       β¬… printed on the claim form
CURRENCY: "RM",                         β¬… or "SGD", "USD", ...
MILEAGE_RATE: 0.60,                     β¬… per km; no quotes around numbers
TIMEZONE: "Asia/Kuala_Lumpur",          β¬… e.g. "Asia/Singapore", "Asia/Jakarta"
Categories, limits and the other options can wait. Section 10 explains them. The submit-to email and your admin ID are not in CONFIG: they go into Script Properties (next step and Step 5).

Click πŸ’Ύ Save again.

Don't put your token or key in CONFIG. The code doesn't even look for them there. They go in the next step.

3d. Save the token and key in Script Properties

Script Properties is a small, private storage area attached to your script. It isn't part of the code, so the code can be copied or updated without exposing your secrets.

  1. In the Apps Script left sidebar, click the βš™οΈ gear icon (Project Settings).
  2. Optional: under General settings, check that Time zone is yours, e.g. (GMT+08:00) Kuala Lumpur.
  3. Scroll to the bottom, to Script Properties, and click Add script property.
  4. Add these three properties. Type the names exactly, in capitals with underscores:
Script Properties: three rows (a fourth comes in Step 5)
PropertyValue
TELEGRAM_BOT_TOKENpaste the token from Step 1
OPENAI_API_KEYpaste the key from Step 2 (starts with sk-)
SUBMIT_TO_EMAILwho receives /submit claim emails, e.g. accounts@mycompany.com. Use your own email while testing.
⚠ No spaces before or after. Then click Save script properties.
  1. Click Save script properties.
  2. Delete your temporary note with the token and key. You won't need it again.
What you should see:Project Settings page, Script Properties section with the three property names visible and the values blurred

Why Script Properties and not CONFIG? Settings that are personal or sensitive (keys, your Telegram ID, the claims email) stay out of the code. The code has no built-in admin ID or email to fall back on: the bot reads them only from Script Properties. So you can share or update Code.gs without exposing them, and editing the code can't change where claims are emailed. Setup stops with a clear message if SUBMIT_TO_EMAIL is missing or isn't an email address. To change the email later, use the Sheet menu Claim Bot β†’ Set submit-to email (or edit the property). Whenever the admin ID or the email changes, the bot messages the previous admin on Telegram, so a quiet change by another Sheet editor doesn't go unnoticed.

7. Step 4: Run setup and allow Google permissions (5 min)

  1. Click the < > Editor icon in the left sidebar to go back to the code.
  2. In the toolbar above the code there's a drop-down showing a function name. Choose setup.
  3. Click β–Ά Run.
What you should see:Editor toolbar with the function drop-down set to "setup" and the Run button highlighted

The permission screens (don't panic)

The first time you run it, Google asks you to approve what the script may do. This is normal for any Apps Script.

  1. "Authorization required": click Review permissions.
  2. Choose your Google account.
  3. "Google hasn't verified this app": click Advanced (small link, bottom left), then Go to Claim Bot (unsafe).
  4. A list of permissions appears. If you see checkboxes, tick Select all. Then click Allow (or Continue).

Why "unverified" and "unsafe"? Google shows this for every private script that hasn't gone through Google's app review. Review is meant for apps published to the public. Here, you are both the developer and the only user: the script runs in your account and sends your data only to Telegram and OpenAI. You can read the code yourself, because it's all in one file. Never click through this warning for scripts from people you don't trust.

What each permission is for:

Google asks to… Why the bot needs it
See, edit, create and delete your spreadsheets Write claim rows; build the Excel claim form
See, edit, create and delete your Google Drive files Save receipts into the bot's folder; trash a receipt when you Undo
Connect to an external service Talk to Telegram and OpenAI
Send email as you /submit emails the claim form
Allow this application to run when you are not present The 1-minute check that keeps the bot running
What you should see:Google "hasn't verified this app" screen with the Advanced link and "Go to Claim Bot (unsafe)" highlighted

Check the result

After you allow access, setup runs (if it doesn't, click β–Ά Run once more). At the bottom, the Execution log should show something like:

Execution log: what success looks like
SETUP COMPLETE

Telegram bot OK: @mycompany_claims_bot
OpenAI key OK. Text model gpt-4o-mini is available
OpenAI key OK. Voice model whisper-1 is available
WARNING: OpenAI will retire ... (shown if OpenAI reports a retirement date)
Text model: gpt-4o-mini ($0.15 / $0.6 per 1M tokens)
Voice model: whisper-1 ($0.006 per minute)
Sheet ready: Claim Bot Data (tabs: Claims, STAFF, SUBMISSIONS, TOKEN_LOG, CHAT_LOG)
Drive folder ready: Claim Bot Receipts (private: only you can open it)
1-minute trigger created (the bot checks Telegram every minute).
Daily log clean-up: TOKEN_LOG/CHAT_LOG rows older than 90 days are deleted (about 3am).
Submit-to email: accounts@mycompany.com
Email quota left today: 100
Bank details: not collected (default)
AI daily cap: $1/day
REQUIRED NEXT STEP: the admin is not set, so nobody can register yet.
Open @mycompany_claims_bot in Telegram, send /whoami, add the number as
Script Property ADMIN_TELEGRAM_ID ...

If you see a red error instead, it tells you exactly what to fix: usually a mistyped Script Property name or a token copied with a missing character. Fix it and run setup again. Running setup again is always safe; it never deletes data.

Switch back to your Sheet tab. You'll see the new tabs (Claims, STAFF, and so on). Reload the Sheet and a Claim Bot menu appears too, with shortcuts for setup, status, setting the admin ID, the submit-to email and the AI models, pause and uninstall. If you run setup from that menu, it asks for any missing token, key or email in a pop-up box instead.

8. Step 5: Make yourself admin and test it (5 min)

5a. Become the admin

  1. In Telegram, open your bot (search its username, or go to t.me/your_bot_username) and tap Start.
  2. Send /whoami. Wait up to one minute. The bot checks for messages once a minute (Section 12 explains why). It replies: Your Telegram ID: 123456789.
  3. Save that number as your admin ID. Either: - Project Settings β†’ Script Properties β†’ Add script property, then click Save script properties:
PropertyValue
ADMIN_TELEGRAM_ID123456789 (your number from /whoami, digits only)
  1. You don't need to run setup again.
  2. In Telegram, send /start. Answer two questions: your name, then your staff number (or type skip). As admin, you're approved immediately. (The bot does not ask for bank details unless you switch that on; see Section 10.)

5b. Test everything (tick as you go)

What you should see:Telegram chat showing a receipt photo, the "Added - Fuel" reply with the Undo button, and the /list reply
What you should see:The Excel claim form opened in Excel or Google Sheets

All ticked? You're done. πŸŽ‰ Delete the test entries with /undo, or delete the rows in the Sheet, before you start real claims.

9. Daily use: commands and tips

Logging a claim (no command needed)

Send this What happens
πŸ“· Photo of a receipt Read, saved to the Sheet, photo filed in Drive
πŸ“„ PDF invoice (as a file) Same as a photo; saved as .pdf
πŸ—ΊοΈ Google Maps / Waze screenshot of your route Mileage: distance Γ— your rate (e.g. 48 km Γ— RM0.60 = RM28.80)
🎀 Voice note, any language the AI understands (English, Malay, …) Turned into text, then logged
⌨️ Typed text: lunch client RM45 card Logged; no receipt
⌨️ mileage 48km office to Shah Alam Mileage claim

Commands

Command What it does
/start Register (first time) or show the welcome message
/list This month's entries, numbered, with total. /list last = last month, /list 2026-09 = September 2026
/summary Totals by category. Also works with last, -2, or 2026-09
/undo Delete your most recent entry, and trash its receipt
/generate Excel claim form for a month (buttons), or /generate 2026-09
/submit Email the claim form and receipts to the admin-set SUBMIT_TO_EMAIL address, after you confirm. Staff can't change the address
/year 2026 Yearly summary in Excel, with a link to every receipt (handy for tax)
/whoami Shows your Telegram ID
/help Lists all commands
Admin only: /staff List users and their status
Admin only: /generateall One claim form per person, for a month
Admin only: /year 2026 all Yearly summary for everyone

The keyboard buttons under the chat (This Month, Last Month, Claim Form, Help) are shortcuts.

Tips for good results

10. Customising categories, limits and mileage

All of these are in the CONFIG block. After any change, click πŸ’Ύ Save. It takes effect within a minute; you don't need to run setup again (except for WEEKLY_DIGEST and LOG_RETENTION_DAYS).

Categories

The default list suits most freelancers and small companies:

CATEGORIES: ["Meal", "Fuel", "Toll", "Parking", "Mileage",
             "Accommodation", "Transport", "Supplies", "Others"],

You can rename, remove or add categories, for example "Client Entertainment" or "Training". Rules:

CATEGORY_HINTS: { "Client Entertainment": "client lunch, dinner, coffee with customers",
                  "Training": "course fees, seminar, workshop, exam fee" },

Limits (warning only)

Want a warning when a meal is over RM30 or a hotel over RM250 a night?

CATEGORY_LIMITS: { "Meal": 30, "Accommodation": 250 },

The claim is still saved. The reply just adds "Note: above the Meal limit of RM30.00", so the claimant knows before the boss does.

Mileage

Other options

Setting What it does
COMPANY_NAME Header of the claim form and email subject
CURRENCY Shown everywhere: "RM", "SGD", "USD", …
TIMEZONE Used for "today", months and timestamps
MAX_USERS Maximum approved users (default 10)
ALLOWED_USER_IDS Telegram IDs approved automatically, e.g. ["111222333", "444555666"]
Admin ID and submit-to email Not in CONFIG. Change them in Script Properties (ADMIN_TELEGRAM_ID, SUBMIT_TO_EMAIL) or the Sheet menu
DRIVE_FOLDER_NAME Name of the receipts folder (set it before the first setup)
WEEKLY_DIGEST true = each user gets a Sunday-evening recap. Run setup again after changing it
COLLECT_BANK_DETAILS false (default): the bot never asks for bank details. true: /start asks for them with a short privacy notice, and they're printed on the claim form and in the /submit email
DELETE_BANK_REPLY Only used when collecting bank details. true (default): after saving, the bot deletes the person's bank-details message from the Telegram chat and says so. If Telegram refuses, the details are still saved and the person is asked to delete the message themselves
LOG_CHATS true keeps a short CHAT_LOG of conversations for troubleshooting: each entry is cut to about 200 characters, and any run of 8 or more digits (account, phone or ID numbers) is replaced by [n digits hidden]. false turns it off
LOG_RETENTION_DAYS Default 90. Every night, TOKEN_LOG and CHAT_LOG rows older than this are deleted. 0 keeps them forever. Run setup again after changing it. Claims are never deleted
DAILY_COST_ALERT_USD Admin alert when the day's estimated AI cost passes this amount (default 0.50)
DAILY_AI_CAP_USD Daily spending cap (default 1.00). Before each AI call the bot reserves a deliberately high cost estimate and only makes the call if it fits under the cap. When it doesn't, photos and voice notes pause until midnight and the bot tells people to resend tomorrow; typed claims like parking RM5 are still saved using a simple built-in reader. Voice transcription counts too. 0 = no cap
CHECK_AMOUNT_ABOVE Default 1000. A receipt read above this amount gets a "Please check" note (a receipt read as RM0.00 always does). 0 turns the high-amount note off
TEXT_MODEL, VOICE_MODEL Default OpenAI models. To switch without editing code, use Claim Bot β†’ Set AI models. See Section 14
MODEL_PRICES Prices the bot uses for its cost estimates, one row per model. It picks the row for the model in use automatically
AI_PRICE_IN_PER_M, AI_PRICE_OUT_PER_M, VOICE_PRICE_PER_MIN Optional overrides (USD per 1M tokens / per audio minute). Leave them null to follow MODEL_PRICES

Made a typo and now nothing works? A missing comma or quote breaks the whole script. Apps Script usually underlines the problem in red when you save. Compare your line with the examples above. When in doubt, paste a fresh copy of Code.gs and fill in CONFIG again. Your data and Script Properties are not affected.

11. Adding team members

  1. Send your staff the bot link: t.me/your_bot_username.
  2. Each person taps Start, then answers the same two questions (name, staff number), plus bank details only if you turned COLLECT_BANK_DETAILS on.
  3. You, the admin, get a message with Approve and Reject buttons. Tap one. The person is told straight away.
  4. That's it. They can start sending receipts.

Good to know:

12. Privacy and security

Your secrets

Your data

Using it for staff in Malaysia? Staff claims are personal data under Malaysia's Personal Data Protection Act 2010 (PDPA): names, staff numbers, receipts and, if collected, bank details. See Appendix A: PDPA quick notes (Malaysia) at the end of this guide for a plain-language checklist. It's general information, not legal advice.

Who can use the bot

Why replies take up to a minute

The bot doesn't run all the time. Every minute, Google wakes it up for a second or two to check Telegram for new messages. That's why a reply can take up to about 60 seconds.

We chose this on purpose:

Turning it off

You want to… Do this
Pause for a while Run pauseBot (or Sheet menu Claim Bot β†’ Pause bot). Nothing is read or saved while paused, and no AI is used. The bot still checks Telegram every minute and tells each person once that their message wasn't saved and that they should send it again later. After resumeBot, anything sent before the resume is not processed (those senders are told to resend), so a pause is a real stop, not a delay. Messages sent after the resume are handled normally
Remove the bot completely Run uninstall: stops the trigger and deletes the token, key and settings. Your Sheet and receipts are kept
Stop it completely right now (e.g. a leaked token) Send /revoke to @BotFather for a new token, so the old one stops working, then pause the bot
Kill the bot account Send /deletebot to @BotFather
Stop all AI costs Delete the key at platform.openai.com β†’ API keys
Remove Google access myaccount.google.com β†’ Security β†’ Third-party apps β†’ Claim Bot β†’ Remove access

13. Troubleshooting FAQ

Where do I see what went wrong? In Apps Script, click Executions (the β˜°β–Ά icon in the left sidebar). Every run of pollUpdates is listed, and failed ones are red. Click one to read the error. Running checkStatus (or Sheet menu Claim Bot β†’ Check status) also shows a quick health summary.

The bot doesn't reply at all. 1. Wait a full minute; replies aren't instant. 2. Run checkStatus. Is the 1-minute trigger ON? If not, run setup or resumeBot. 3. Check the Executions list for red errors. 4. Did you edit CONFIG and break a comma or quote? Save and look for a red underline.

"Almost ready! The admin is not set yet." Add the number from /whoami as the Script Property ADMIN_TELEGRAM_ID (or use Claim Bot β†’ Set admin Telegram ID in the Sheet), then send /start again.

"You are not registered yet." Send /start and answer the questions. If you're staff, wait for the admin to tap Approve.

Setup says "Telegram token was rejected". The token in Script Properties is wrong or incomplete. Copy it again from @BotFather (or /revoke for a new one), paste it with no spaces, save, and run setup again.

Setup says "OpenAI key was rejected" or the bot says the "credit may be empty". Check that the OPENAI_API_KEY property is correct and that your OpenAI account has credit (platform.openai.com β†’ Billing). A brand-new account sometimes needs a few minutes after payment.

"Missing Script Properties". The property names must be exactly TELEGRAM_BOT_TOKEN and OPENAI_API_KEY: capitals, underscores, no spaces.

The amount or category is wrong. Tap Undo and resend a clearer photo, or simply type the claim. If a category is often wrong, add a hint in CATEGORY_HINTS.

The date is wrong (day and month swapped). Dates are stored as DD/MM/YYYY text on purpose, so Google Sheets can't swap them. If a receipt prints dates the US way (MM/DD), type the claim with the right date instead.

/submit says the email could not be sent. Check that the Script Property SUBMIT_TO_EMAIL is set (run checkStatus to see it). Then run setup once from the editor, in case Google needs you to approve email permission again. Free Gmail accounts have a daily email limit (about 100 recipients).

The email has links instead of attached receipts. Gmail limits the size of attachments. If one month's receipts add up to more than 20 MB, the bot sends private Drive links instead. Your recipient must ask you for access, or you can send the files yourself.

"Could not create the claim form". Usually a temporary Google hiccup. Try again in a minute. If it keeps happening, run setup again to refresh permissions.

A receipt got no reply, or the bot replied twice. The bot is built so that a crash or Google's time limit neither drops nor duplicates a message. It finishes each message first and records its Telegram message number in a short "already done" list (the last 500). Only then does it tell Telegram to move on. If a run stops halfway, the next run (a minute later) picks up the same messages again and skips the ones already done. So a receipt that was interrupted is simply read again, and you may see "Reading your receipt..." twice before one "Added" reply. If a message fails three runs in a row, the bot gives up on it and replies "Sorry, I couldn't process your last message... Please send it again". A real duplicate can only happen in the split second between saving a row and recording it as done. The bot also doesn't spot a receipt you sent twice. In either case use the Undo button, /undo, or delete the extra row in the Sheet. If nothing at all happens for more than 3 minutes, check Executions (see above) and the Claims tab.

Google asks for permission again after I changed something. Normal after you paste a new version of the code. Click through the same screens as in Step 4.

I see "Exceeded maximum execution time" or a quota error. Free Google accounts have daily limits. They reset every 24 hours. If it happens often, set LOG_CHATS: false to reduce work per message.

"Today's AI limit has been reached". The next AI call wouldn't fit under DAILY_AI_CAP_USD (the bot checks before each call). Photos and voice notes work again after midnight (your time zone). Until then, typed claims with an amount still work. If the cap is too low for your team, raise it in CONFIG and click Save. checkStatus shows today's spend against the cap.

A receipt was saved as RM0.00, or says "Please check". The AI couldn't read the total (faded or crumpled receipt), or read an amount above CHECK_AMOUNT_ABOVE. The row is saved so nothing is lost. If it's wrong, tap Undo and type the claim instead, e.g. fuel RM50.

"The claim bot is paused" / "was paused when you sent...". The admin ran pauseBot. Nothing sent during the pause was saved. Send it again once the bot is back on.

The previous admin got a "setting was changed" message. Someone changed ADMIN_TELEGRAM_ID or SUBMIT_TO_EMAIL (from the Sheet menu or in Script Properties). If that wasn't you, check Project Settings β†’ Script Properties and who can edit the Sheet.

Typed claim says "I couldn't find an amount". Include a number with the currency, e.g. toll RM12.50 or mileage 48km. Nothing is saved without an amount.

Can I use it in a Telegram group? No. It only works in private chats, so each person's claims stay private.

Can I use a different AI provider? The code is built for OpenAI's API. You can change the model names (Section 14), but not the provider, without editing code.

14. Updating the bot and keeping it running

When OpenAI retires a model

OpenAI regularly retires older models. As of October 2026 it has announced:

Setting Default in v1.2 Retirement date OpenAI's recommended replacement Price (standard, as listed 17 Oct 2026)
TEXT_MODEL gpt-4o-mini (US$0.15 / 0.60 per 1M tokens) 14 January 2027 gpt-6-luna US$0.10 in / US$0.50 out per 1M tokens
VOICE_MODEL whisper-1 (US$0.006 per minute) 26 February 2027 gpt-transcribe (or gpt-live-transcribe for live audio) gpt-transcribe: US$0.0045 per minute (estimated)

Sources: platform.openai.com/docs/deprecations, platform.openai.com/docs/pricing, and the model pages for gpt-6-luna and gpt-transcribe. Prices change, so check them again before you switch.

To switch (no code editing):

  1. In the Sheet, choose Claim Bot β†’ Set AI models.
  2. Type the new text model (e.g. gpt-6-luna) and the new voice model (e.g. gpt-transcribe), or leave a box empty to keep the current one.
  3. The bot checks with OpenAI that your key can use each model, and saves it only if it can. The choice is stored as Script Properties TEXT_MODEL / VOICE_MODEL.
  4. Send a test receipt photo, a test PDF and a test voice note.

Typing default goes back to the model in CONFIG. setup checks both the text model and the voice model, and warns if OpenAI has announced a retirement date. Check the current list at platform.openai.com/docs/deprecations.

Costs follow the model automatically. MODEL_PRICES in CONFIG already lists gpt-4o-mini, gpt-6-luna, whisper-1 and gpt-transcribe, and the bot uses the row for the model in use, both for TOKEN_LOG and for the daily cap. If you choose a model that isn't listed, the bot uses a deliberately high estimate and says so in checkStatus; add a row with its price from OpenAI's pricing page. To force your own figures, set AI_PRICE_IN_PER_M, AI_PRICE_OUT_PER_M or VOICE_PRICE_PER_MIN.

Honest note on testing: this bot is based on a claim bot the author built and uses. This generic v1.2 edition has so far been checked only with a simulated test harness (fake Telegram, Google and OpenAI). It has not yet been run end-to-end on live services, and neither have the newer models. Things to watch when you switch:

If anything misbehaves, use Set AI models β†’ default to switch back while the old model is still available.

Installing a newer version of Code.gs

  1. Copy your current CONFIG values into a note: company, categories, limits, etc. The token, key, admin ID, submit-to email and model choices are in Script Properties and survive the update. Coming from v1.0? It kept SUBMIT_TO_EMAIL and ADMIN_TELEGRAM_ID in CONFIG. Add them as Script Properties first; v1.2 no longer reads them from CONFIG. Also rename AI_MODEL to TEXT_MODEL if you changed it.
  2. In the editor, select all, delete, and paste the new Code.gs.
  3. Fill CONFIG back in, then click Save.
  4. Run setup once and allow permissions if asked.

Your Sheet data, Drive receipts and Script Properties are untouched.

Healthy-bot habits

15. Final checklist

Accounts

Google

Telegram

Team (optional)

Security

Ready to build it?

Get Code.gs, the licence, a README and a PDF copy of this guide in one download, then follow the steps above. Setup typically takes about 30 minutes of hands-on time.

Get the code β†’

Telegram, Google, Gmail, Google Sheets, Google Drive, Apps Script and OpenAI are trademarks of their respective owners. This guide is independent and not affiliated with or endorsed by them. Prices and screens were checked in October 2026 and may change.

Appendix A: PDPA quick notes (Malaysia)

A plain-English PDPA 2010 (as amended 2024) checklist.

What the bot holds: staff names, Telegram IDs and optional staff numbers (STAFF), claims (Claims, SUBMISSIONS), logs (CHAT_LOG keeps a short copy of each message, about 200 characters, with runs of 8+ digits shown as "[n digits hidden]"; TOKEN_LOG: AI usage) and receipts in a private Drive folder. OpenAI reads receipts, voice notes and typed claims. You are the data controller.

1. Bank details: leave them off. The bank-details setting is off by default. If you turn it on, give staff a notice first (step 2); the bot asks in the private chat and staff can type skip. The answer is not written to CHAT_LOG, and the delete-bank-reply setting (on by default) then deletes it from the chat. Messages older than 48 hours can't be deleted; the bot then asks the person to delete it. Keep the STAFF tab protected. When on, they appear on the claim form and /submit email.

2. Tell staff before they start. Give a BM/English notice; record who received it. Sample:

"The claim bot records your name, Telegram ID, claims, receipt photos and voice notes (transcribed; audio not kept) [and bank details, if you choose to give them] so that we can process expense claims. OpenAI reads it and Google stores it, possibly outside Malaysia. Telegram carries the messages. Only [admin/accounts] can see all claims. Logs are kept for [N] days, and claims and receipts for [period required for tax]. To see or correct your data, contact [name/role]."

Also record your basis for transfers outside Malaysia (usually staff consent in this notice, or necessity for processing claims). OpenAI may keep API inputs for a limited period under its terms; photos and voice notes also stay in the Telegram chat unless deleted.

3. Control who has access.

4. Keep data only as long as needed.

5. Keep the Drive folder private. Keep sharing Restricted; never "Anyone with the link".

6. Rotate keys when in doubt. Rotate if a token or key was exposed, an editor leaves, or costs look wrong.

Keep the daily spend cap setting on (the cost alert warns earlier). pauseBot() stops processing: the timer keeps running, but messages sent while paused are skipped, never saved later, and each sender is asked to resend. To stop completely, revoke the token.

7. Breach checklist (e.g. leaked token or key, misdirected claim email, shared folder).

  1. Contain: pauseBot() or revoke the token, remove sharing, recall the email.
  2. Rotate any exposed token or key.
  3. Assess: notifiable if it causes or is likely to cause significant harm (e.g. financial loss, identity fraud from bank details) or affects more than 1,000 people.
  4. If notifiable, tell the Commissioner as soon as practicable, within 72 hours (the guideline's examples count from when you become aware).
  5. If significant harm is likely, tell affected staff without unnecessary delay, within 7 days of notifying the Commissioner.
  6. Log every breach in a register, notifiable or not.

Not notifying the Commissioner of a notifiable breach: fine up to RM250,000 and/or 2 years.

8. Do you need a DPO? Required above 20,000 data subjects, 10,000 for sensitive (incl. financial) data, or with regular and systematic monitoring. Record your check.

Sources: PDPA as amended (Act A1727) Β· Breach notification guideline Β· DPO guideline Β· Cross-border transfer guideline. Check pdp.gov.my for updates. Not legal advice.