SMS OTP to Telegram — Setup Guide¶
Automatically forward bank OTP codes from your phone to Telegram so your bank scraper can read them without manual input.
Alternative: native app OTP (no Telegram)¶
The mobile app can collect OTP codes directly, so you do not need Telegram or an SMS-forwarding automation. Set the OTP delivery channel to app from the mobile app — this setting is app-only and is intentionally not exposed in the web portal.
How it works:
Bank login needs an OTP
↓
Importer records a pending request and pushes a prompt to the app
↓
You open the app and enter the code (it also polls for pending requests)
↓
App submits the code to the importer, which continues the login
When the channel is app and Telegram is also configured, a timed-out app OTP automatically falls back to Telegram. A storage failure does not: when the importer cannot record the expiry and your code has not arrived, or cannot read an answer that may hold your code, the login fails with the storage error.
Configuration¶
- Choose the channel from the mobile app (OTP delivery → App). It is stored server-side, outside the config manifest, so the web portal never shows it.
- The importer and portal coordinate through files on the data volume both mount at
/app/data(importer-datain the shippeddocker-compose.yml). Defaults are shown; if you override one, set the same path on both services: OTP_REQUESTS_PATH— where OTP requests are kept (default/app/data/otp-requests.json). Each request is its own file beside this path,otp-requests.<id>.json, and its answer isotp-requests.<id>.answer.json.OTP_SETTINGS_PATH— the selected channel (default/app/data/otp-settings.json)- Every file is owner-only (
0600) and appears whole or not at all. A request's answer is written once, by whichever comes first: the portal with your code, or the importer marking the request expired. The OTP files need a data volume with hard links. Local disks and Docker volumes have them; an SMB/CIFS share such as Azure Files does not. There a bank login that asks for an app code fails with the storage error (it does not fall back to Telegram), and the portal answers a submitted code with 500. - Run the same release on the importer and the portal, and do not roll back: an earlier release does not see this release's requests. See Upgrade both services together.
Portal endpoints (used by the app)¶
GET /api/otp/pending— list pending OTP requests (never returns codes)POST /api/otp/:id— submit a 4–8 digit code for a request.404means the request is gone, expired or already answered;400means the id is malformed (Invalid OTP request id) or the code is (Invalid OTP code). A code sent at the moment the importer finishes with that request can get500instead of404; it was too late either way. When the importer cannot write to the data volume, a code sent as it gives the request up gets404and is removed; rarely, so does a code it did useGET /api/otp/settings/PUT /api/otp/settings— read or set the channel
Security¶
- A code stays on disk only between submission and use: once the importer takes it, the answer is overwritten with a marker that keeps the request answered, and any copy that saving the answer left behind is removed. A code sent after the importer has given the request up is removed at once. Codes are single-use, expire with the request (default 5 minutes), and are never logged. When either service starts, it removes requests and answers an hour past their deadline, and leftover copies an hour after they were written, so a code nobody used, or a copy that could not be removed, leaves the disk too.
- All OTP endpoints sit behind the portal's authentication, and request ids are unguessable UUIDs.
What it does¶
Bank sends SMS with OTP code to your phone
↓
Your phone detects the SMS automatically
↓
Extracts the numeric code (e.g. 848156)
↓
Replies to the Telegram bot notification with the code
↓
Your bank scraper reads the code automatically
Important: How Telegram OTP forwarding works¶
The bot cannot read messages from other bots — only from you (the user). So the phone must send the OTP code as you by replying to the bot's Telegram notification. This is why we use MacroDroid's Notification Reply action instead of the HTTP/Bot API approach.
Requirements for Notification Reply to work:
- The Telegram bot must send a notification asking for OTP (this creates a reply-able notification)
- The SMS must arrive while the notification is still active
- MacroDroid must have Notification Access permission
- Telegram must NOT be open in the foreground (notifications don't appear when the chat is open)
Android Setup (MacroDroid)¶
Prerequisites¶
- Android phone (tested on Samsung Galaxy S24)
- MacroDroid app (free from Google Play Store)
- An Israeli bank importer bot configured with
twoFactorAuth: true
Step 1: Install MacroDroid¶
- Open Google Play Store
- Search for MacroDroid
- Install it (free — up to 5 macros)
- Open the app
Step 2: Grant Permissions¶
When prompted, allow:
- SMS — Read & Receive
- Notification access: Settings → Apps → Special access → Notification access → enable MacroDroid
- Accessibility: Settings → Accessibility → Installed Apps → MacroDroid → enable
Step 3: Create New Macro¶
- Tap the + button (Add Macro)
- Name it:
SMS Code Copier
Step 4: Add Trigger¶
- Tap Triggers → +
- Select Phone/SMS → SMS Received
- Select Any Number
- Content matching: select Contains
- Enable Regex
- Enter:
\d{5,6}(matches any 5-6 digit number) - Tap OK
Step 5: Add Actions¶
Action 1 — Set Variable (capture SMS text)¶
- Tap Actions → +
- Select Variables → Set Variable
- Variable name:
sms - Value:
[sms_message](tap magic wand → SMS → SMS Message Body) - Tap OK
Action 2 — Wait 50ms¶
- Tap Actions → +
- Select Conditions/Loops → Wait
- Set: 50 milliseconds
- Tap OK
Action 3 — Extract Text (extract OTP code)¶
- Tap Actions → +
- Select Variables → Extract Text
- Source:
[lv=sms] - Pattern/Regex:
\d{4,10}(matches 4-10 digit codes) - Save result to variable:
numbers - Tap OK
Action 4 — Wait 50ms¶
- Tap Actions → +
- Select Conditions/Loops → Wait
- Set: 50 milliseconds
- Tap OK
Action 5 — Copy to Clipboard¶
- Tap Actions → +
- Select Device Actions → Fill Clipboard
- Text:
[lv=numbers] - Tap OK
Action 6 — Wait 50ms¶
- Tap Actions → +
- Select Conditions/Loops → Wait
- Set: 50 milliseconds
- Enable Use alarm (keeps running even if phone sleeps)
- Tap OK
Action 7 — Show Popup (optional, for visual confirmation)¶
- Tap Actions → +
- Select Device Actions → Popup Message
- Text:
Code Copied: [lv=numbers] - Tap OK
Action 8 — Clear SMS Notification¶
Clears the bank SMS notification matching the OTP code (within last 60 seconds).
- Tap Actions → +
- Select Device Actions → Clear Notifications
- Select application: Messages (your SMS app)
- Match text:
{lv=numbers} - Enable Regex: Yes
- Age: 60 seconds
- Tap OK
Action 9 — Notification Reply (send OTP to Telegram)¶
This is the key action — it replies to the Telegram notification as YOU (the user), not as a bot.
- Tap Actions → +
- Select Notification Reply
- Configure:
- Application: Telegram
- Reply text:
{lv=numbers} - Tap OK
Note: Telegram must appear in the app list. If it doesn't, make sure you have an active Telegram notification in your notification bar, then try again.
Important: The Notification Reply action uses
{lv=numbers}(curly braces), not[lv=numbers](square brackets). This is different from other MacroDroid actions.
Action 10 — Wait 50ms¶
- Tap Actions → +
- Select Conditions/Loops → Wait
- Set: 50 milliseconds
- Enable Use alarm
- Tap OK
Action 11 — Clear Telegram OTP Notification¶
Clears the bot's "Enter OTP code" notification after the code has been sent.
- Tap Actions → +
- Select Device Actions → Clear Notifications
- Select application: Telegram
- Match text:
🔐 Enter OTP code for - Age: 1 second
- Tap OK
Step 6: Save and Enable¶
- Tap the checkmark (top right) to save
- Make sure the macro toggle is ON
- You should see a persistent notification: "MacroDroid is running"
Step 7: Prevent Android from Killing the App¶
This is critical — without these settings, your phone will kill MacroDroid in the background.
Samsung Galaxy (One UI):
- Settings → Apps → MacroDroid → Battery → select Unrestricted
- Settings → Device Care → Battery → Background usage limits → make sure MacroDroid is NOT in the sleeping or deep sleeping list
- Open Recent Apps → find MacroDroid → tap the lock icon to pin it
Xiaomi (MIUI):
- Security app → Permissions → Autostart → enable MacroDroid
- Settings → Battery → App Battery Saver → MacroDroid → No restrictions
- Disable Verification Code Protection: Settings → Passwords & Security → Privacy
Huawei (EMUI):
- Settings → Battery → App Launch → MacroDroid → set to Manual → enable all toggles
- Settings → Security → disable verification code protection
Step 8: Test¶
Important: The "Test Macro" button will NOT work — it doesn't have real SMS data. Only real incoming SMS triggers correctly.
End-to-end test:
- Make sure Telegram is NOT open (minimize it / press Home)
- Trigger an import from Telegram: send
/scan oneZeroto the bot - Wait for the bot to send "Waiting for OTP code..." — this creates a notification on your phone
- Do NOT dismiss the notification
- The bank sends SMS with OTP code
- MacroDroid extracts the code → clears SMS notification → replies to the Telegram notification → clears OTP notification
- The importer reads the code and completes the import
Verify:
- Check your Telegram chat — you should see the OTP code as a message from you (not from the bot)
- The importer should show "OTP received" in the logs
Android Action Flow¶
SMS Received containing 5-6 digits (trigger)
↓
[1] Set Variable → sms = full SMS text
↓
[2] Wait 50ms
↓
[3] Extract Text → numbers = OTP code (4-10 digits)
↓
[4] Wait 50ms
↓
[5] Fill Clipboard → copies OTP to clipboard
↓
[6] Wait 50ms
↓
[7] Popup Message → shows "Code Copied: 848156"
↓
[8] Clear SMS Notif → removes bank SMS notification
↓
[9] Notification Reply → sends OTP to Telegram AS YOU
↓
[10] Wait 50ms
↓
[11] Clear Telegram → removes "Enter OTP code" notification
Android Troubleshooting¶
| Problem | Solution |
|---|---|
| SMS not detected | Check SMS permissions: Settings → Apps → MacroDroid → Permissions → SMS |
| Code not extracted | The SMS may not contain a 5-6 digit number, or the regex doesn't match |
| Notification Reply doesn't send | Make sure Telegram notification is visible (not dismissed). Telegram must NOT be open in foreground |
| Reply sends wrong text | Check variable syntax: use [lv=numbers] or {lv=numbers} depending on MacroDroid version |
| App killed after hours | Disable battery optimization + lock in recents (see Step 7) |
| Test button doesn't work | Normal — only real SMS triggers work, not the test button |
| Amex marketing SMS triggers macro | The regex \d{5,6} may match phone numbers in ads. Consider filtering by sender |
| Xiaomi blocks OTP reading | Disable "Verification Code Protection" in Settings → Security |
| Telegram notification has no Reply button | Enable Direct Reply: Settings → Notifications → Advanced → Direct reply |
iPhone Setup (iOS Shortcuts)¶
No jailbreak or third-party apps needed. Uses the built-in Shortcuts app.
Note: On iPhone, Shortcuts sends the full SMS text (not just the extracted code). The importer must be able to parse the code from the full message.
Prerequisites¶
- iOS 18 or later (required for "Run Immediately" on message triggers)
- Shortcuts app (built-in, pre-installed)
- Telegram bot token and chat ID
Step 1: Open Shortcuts App¶
- Open the Shortcuts app on your iPhone
- Tap the Automation tab at the bottom
Step 2: Create New Automation¶
- Tap the + button (top right)
- Select Message from the list of triggers
Step 3: Configure the Trigger¶
- Sender: leave empty (catches SMS from any sender)
- Message Contains: tap and enter
קוד - This filters for Hebrew OTP messages containing "code"
- Alternatively, leave a blank space to forward ALL SMS
- Tap Next
Step 4: Set to Run Immediately¶
This is the most important step — it makes the automation fully automatic.
- Select "Run Immediately"
- Toggle OFF "Notify When Run" (optional — disables the notification banner)
- Tap Next
Without "Run Immediately", iOS will show a notification that you must tap to confirm — defeating the purpose of automation.
Step 5: Create the Action¶
- Select "New Blank Automation"
- Tap "Add Action"
- Search for "Get Contents of URL"
- Tap to add it
Step 6: Configure the HTTP Request¶
- Tap the URL field and enter:
-
Tap "Show More" to expand options
-
Set Method:
POST -
Set Headers:
- Tap "Add New Header"
- Key:
Content-Type -
Value:
application/x-www-form-urlencoded -
Set Request Body: select "Form"
-
Add form fields:
- Tap "Add New Field" → Key:
chat_id→ Value:<CHAT_ID> - Tap "Add New Field" → Key:
text→ tap here, then tap "Shortcut Input" from the variable suggestions above the keyboard (this inserts the SMS message body)
Replace
<BOT_TOKEN>and<CHAT_ID>with your actual values.Note: On iPhone, the message is sent via Bot API (as the bot, not as you). This only works if the importer is configured to read bot messages in a group chat. See the "iPhone Limitation" section below.
Step 7: Save¶
- Tap "Done" (top right)
- The automation should appear in your Automation list
- Verify it shows "When I receive a message..." with "Runs Immediately"
Step 8: Test¶
- Send yourself an SMS from another phone containing the word
קודand a number:
- Check your Telegram — the full SMS text should appear in your chat
iPhone: Extract Just the OTP Code (Advanced)¶
If you want to send only the 6-digit code instead of the full SMS:
- Before the HTTP request, add action: "Match Text"
- Input: Shortcut Input
- Pattern:
\d{5,6} - In the HTTP request, for the
textfield value: - Use "Matches" (output from Match Text) instead of "Shortcut Input"
iPhone Action Flow¶
SMS Received containing "קוד" (trigger)
↓
Runs Immediately (no user interaction)
↓
[1] Get Contents of URL → POST to Telegram Bot
iPhone Limitation¶
On iPhone, the message is sent via Bot API (as the bot), not as you. Telegram bots cannot read messages from other bots in private chats. To work around this:
- Create a Telegram group
- Add the importer bot to the group
- Disable bot privacy mode via @BotFather (
/setprivacy→ Disable) - Use the group chat ID in the HTTP request
Note: Even with this workaround, bots cannot see messages from other bots in groups. The iPhone Bot API approach may not work for OTP forwarding. The Android Notification Reply method is the only confirmed working solution for fully automatic OTP forwarding.
iPhone Troubleshooting¶
| Problem | Solution |
|---|---|
| Automation asks for confirmation tap | Make sure "Run Immediately" is selected (Step 4). Requires iOS 18+ |
| Nothing happens when SMS arrives | Check that the trigger keyword matches. Try blank space instead of קוד |
| HTTP error in Shortcuts | Verify bot token and chat ID. Test the URL in Safari |
| "Shortcut Input" not available | Only appears inside a Message automation trigger |
| Automation doesn't run when locked | Should work on iOS 18+. Check Shortcuts has permissions |
| Hebrew text garbled in Telegram | HTTP POST with form encoding handles UTF-8 correctly |
Platform Comparison¶
| Feature | Android (MacroDroid) | iPhone (iOS Shortcuts) |
|---|---|---|
| Fully automatic OTP | Yes (Notification Reply) | Limited (Bot API only) |
| Sends as user | Yes | No (sends as bot) |
| No third-party app needed | No (Play Store) | Yes (built-in) |
| Background running | Yes (with battery settings) | Yes |
| Filter by content | Yes | Yes |
| Extract OTP code only | Yes (Extract Text) | Yes (Match Text) |
| Cost | Free | Free |
| Clipboard copy | Yes | No (add extra action) |
Security Notes¶
- On Android, MacroDroid uses Notification Reply which goes through Telegram's notification system — the message is sent as you, encrypted in transit
- OTP codes expire in 2-5 minutes so even if intercepted, they're useless quickly
- Use a dedicated Telegram bot for OTP forwarding only
- MacroDroid requires SMS read permissions — use only trusted apps (MacroDroid has millions of Play Store downloads)
- iOS Shortcuts is Apple's built-in automation tool — maximum trust
- Neither method sends data through third-party servers