Webhooks
Any HTTP call becomes a text from your phone
One address, no key, no code
In your dashboard, make a webhook with the numbers that should get its texts, and paste its address into any tool that can send an HTTP request: Grafana, Uptime Kuma, Prometheus Alertmanager, Healthchecks, Gatus, Odoo Online, GitHub Actions, cron jobs, your own scripts. Each call becomes a text from your own phone, from your own number.
How it stays safe
The numbers are saved in the webhook
A call can only change what the text says, never who gets it. Numbers are never read from the call.
This is deliberate. A webhook address tends to end up in config files, dashboards and screenshots. If a call could choose the number, anyone who saw the address could text any number at your expense, including premium-rate numbers. Webhooks are for alerts to yourself and your team.
20 by default, up to 500. The limit counts calls, not texts: each call texts every number of the webhook. Past it, calls are refused until the hour is over, so an alert storm can't use up your texts.
Copy the address again whenever you need it. If it leaks, New address replaces it and the old one stops working at once.
Each number gets its own text, from your number. Through the API, a webhook can also take calls only from some IPs or IPv4 ranges, like 10.0.0.0/8.
1. Make a webhook in your dashboard
- 1. Install the SMSTunnel app from Google Play on your Android phone and sign in.
- 2. In your dashboard, open Apps → Webhooks and click New webhook.
- 3. Pick what will call it: Grafana, Uptime Kuma, Healthchecks, Gatus, Alertmanager, Odoo Online, or Something else (a script, a cron job, any other tool). That fills in the name and, where the tool needs one, the text of the message.
- 4. Add the numbers that get the texts, with
+and the country code: up to 10, and each one gets its own text. Change Calls an hour, at most if 20 doesn't suit you, and click Create the webhook. - 5. Copy the address. For the tool you picked, the dashboard shows its setup steps with the address already in them, and Send a test, which texts the webhook's numbers and counts toward its hour.
It opens New webhook with Something else picked; pick your tool instead if it's in the list. Each setup below has a button that picks its tool.
2. Call it
Paste the address into your tool, or call it yourself:
curl -X POST https://smstunnel.io/api/v1/webhook/<token> \
-H "Content-Type: application/json" \
-d '{"message": "Backup finished successfully"}'
A plain GET works too, for tools that can only open a URL: the query string becomes the
call, as in …/webhook/<token>?message=Disk+almost+full.
3. What the text says
The tool's own words
Leave the text of the message empty, and SMSTunnel writes the text from the call:
- Prometheus Alertmanager and Grafana: status, alert name and summary, like
[FIRING] DiskFull - Disk 95% full: /volume1, with "(+2 more)" for a group. - Uptime Kuma: its alert already starts with the monitor's name, so picking Uptime Kuma sets
the text of the message to
{{msg}}. Without it, the name shows twice. - Any other tool: the first of its
message,text,msg,title,body,content,summaryorsubjectfields.
Your own words
Write the Text of the message (messageTemplate in the API), up to 500
characters, and put the call's fields between double braces. A dot goes one level into the JSON, a number picks
an item of a list, and a field missing from the call stays empty:
[{{status}}] {{alerts.0.labels.alertname}} on {{alerts.0.labels.instance}} Texts are cut at 480 characters: alerts should be short, and it keeps one noisy call from becoming several SMS.
Ready-made setups
Uptime Kuma
Settings → Notifications → Set Up Notification: Notification Type Webhook, Post URL the address, Request Body Preset - application/json. Picking Uptime Kuma sets the webhook's text of the message to {{msg}}.
Grafana
Alerting → Contact points → + Add contact point, Integration Webhook, with the address in URL and the other fields as they are. Any Grafana version, Grafana Cloud too.
Prometheus Alertmanager
A receiver with the address, on any Alertmanager version. Route the alerts that deserve a text to it:
receivers:
- name: sms
webhook_configs:
- url: https://smstunnel.io/api/v1/webhook/<token> Make a webhook for Prometheus Alertmanager The Prometheus Alertmanager guide
Healthchecks.io
Integrations → Add Integration → Webhook, with GET and the address plus the words, when a check goes down and when it goes up:
https://smstunnel.io/api/v1/webhook/<token>?message=$NAME+is+DOWN
https://smstunnel.io/api/v1/webhook/<token>?message=$NAME+is+back+up Make a webhook for Healthchecks.io The Healthchecks.io guide
Gatus
The custom alerting provider, with the address, the Content-Type line and a body with only the words:
alerting:
custom:
url: "https://smstunnel.io/api/v1/webhook/<token>"
method: "POST"
headers:
Content-Type: "application/json"
body: |
{"message": "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION]"} Odoo Online
An automation rule with the action Send Webhook Notification, and a text of the message like New order {{name}}: {{amount_total}}. Texts to your own numbers, never to the customer of the record.
GitHub Actions
A step that runs when a job fails, with the address in the repository secret SMS_WEBHOOK:
- name: Notify on failure
if: failure()
run: |
curl -X POST "${{ secrets.SMS_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{"message":"Build failed on ${{ github.repository }}"}' Cron or any script
A text when the nightly job fails. In crontab the whole command stays on one line: cron doesn't join lines that end with a backslash.
0 3 * * * /opt/backup.sh || curl -s -X POST https://smstunnel.io/api/v1/webhook/<token> -H 'Content-Type: application/json' -d '{"message":"Nightly backup FAILED"}' Managing webhooks
Apps → Webhooks lists your webhooks with the numbers they text, when each was last used, its calls this hour and, if its last call sent no text, why. For each one:
- Address and setup shows the address again, ready to copy, with the steps for each tool and Send a test.
- Edit changes the name, the numbers, the text of the message and the calls an hour. The address stays the same, so your tools keep working.
- Stop refuses its calls until you Start again.
- New address replaces the address: the current one stops working at once, so put the new one in every tool that calls it.
- Delete removes it, and its address stops working.
Or with the API, for scripts
A script can make and manage webhooks too, with your account's sign-in token rather than an API key (the
accessToken that POST /api/v1/auth/login answers with):
curl -X POST https://smstunnel.io/api/v1/webhooks \
-H "Authorization: Bearer <sign-in token>" \
-H "Content-Type: application/json" \
-d '{"name": "Server alerts", "recipients": ["+14155550123"], "maxPerHour": 20}' The answer has the secret token:
{
"id": "...",
"token": "a1b2c3...",
"url": "/api/v1/webhook/a1b2c3...",
...
}
The address is https://smstunnel.io/api/v1/webhook/<token>. Besides name and
recipients (1 to 10), it takes messageTemplate,
maxPerHour (1 to 500), allowedIps, deviceId,
defaultCountryCode and enabled.
| Endpoint | Purpose |
|---|---|
| GET /api/v1/webhooks | List, with usage counters |
| GET /api/v1/webhooks/:id | One webhook, with its token |
| PATCH /api/v1/webhooks/:id | Change recipients, template, limits |
| POST /api/v1/webhooks/:id/rotate | Issue a new secret address |
| DELETE /api/v1/webhooks/:id | Remove it |
All of them take your account's sign-in token, not an API key.
Build it with an AI assistant
Copy this prompt into Claude, ChatGPT, Cursor or Copilot and replace the part in [brackets]. Don't paste your API key into the chat: the prompt doesn't need it.
Help me turn alerts from my own tools into text messages with an SMSTunnel webhook.
About SMSTunnel (https://smstunnel.io): my own apps and tools hand text messages (SMS) to my SMSTunnel account, and my own Android phone sends them from my own number, on my own SIM and plan. The phone runs the SMSTunnel app from Google Play (https://play.google.com/store/apps/details?id=io.smstunnel.app), signed in to my account.
What I want: [describe what should send a text, to whom, and when]
How it works:
- An SMSTunnel webhook is a secret address, https://smstunnel.io/api/v1/webhook/<token>. A POST to it (JSON or a URL-encoded form; multipart/form-data is not read) or a GET (query string) texts the numbers saved in the webhook; the call only sets the wording, never the numbers.
- I make it in my dashboard (https://dashboard.smstunnel.io): Apps → Webhooks → New webhook. I pick the tool that will call it (Grafana, Uptime Kuma, Healthchecks, Gatus, Alertmanager, Odoo Online, or Something else), which fills in the name and, where the tool needs one, the text of the message; then 1 to 10 numbers (each gets its own text), the optional text of the message (up to 500 characters) and the calls an hour (1 to 500, default 20), and Create the webhook. The dashboard then shows the address, the setup steps of the chosen tool with the address in them, and Send a test (a real call: it texts every number and counts toward the hour).
- Later, in Apps → Webhooks: Address and setup shows the address again; Edit changes the name, the numbers, the text and the calls an hour (the address stays the same); Stop and Start again; New address replaces the address (the old one stops working at once); Delete.
- For scripts, the same with the API: POST https://smstunnel.io/api/v1/webhooks with the header "Authorization: Bearer <my SMSTunnel sign-in token (JWT)>" (an API key does not work here; sign in again if it answers 401), body {"name": "...", "recipients": ["+14155550123"]} plus optional "messageTemplate", "maxPerHour", "allowedIps" (IPs or IPv4 ranges like 10.0.0.0/8; only through the API), "deviceId", "defaultCountryCode", "enabled". The answer has the token and the url. Other fields are refused with 400. Manage: GET /api/v1/webhooks (list), GET, PATCH and DELETE /api/v1/webhooks/:id, POST /api/v1/webhooks/:id/rotate (a new address).
- The text: without a template, Prometheus Alertmanager and Grafana payloads are recognised ("[FIRING] DiskFull - Disk 95% full"), and Uptime Kuma's too, but its msg already starts with the monitor's name, so for Uptime Kuma the text of the message is {{msg}}; otherwise the first of message, text, msg, title, body, content, summary, subject is used. A template pulls fields with {{path}}, numbers pick array items: "[{{status}}] {{alerts.0.labels.alertname}}". Texts are cut at 480 characters.
- Answers: 200 {"success", "sent", "total", "results"} (numbers masked); 404 unknown or stopped webhook; 403 IP not allowed; 429 over its calls an hour (the limit counts calls, not texts: each call texts every number), or over 60 calls a minute to the webhook; 400 when no text can be built. If my phone is offline, the texts wait and go out when it is back.
Rules for your answer:
- Do not ask for my password or sign-in token, and do not put real secrets in your answer.
- Treat the webhook address as a secret: keep it in the tool's secret settings, not in a public repository; make a new address if it leaks.
- Keep alerts short, and help me avoid floods of texts (grouping, the calls an hour).
- Webhooks are for alerts to me and my team, who expect them: the numbers are saved in the webhook, never taken from the call. No unsolicited texts. Responsible use
Webhooks are meant for alerts to yourself and your team, who expect them. Sending unsolicited messages from a consumer SIM generally breaches mobile carrier terms: make sure your usage complies with local regulations.
Prefer a visual workflow?
The n8n node gives you the same power with a drag-and-drop editor.
See the n8n integration