SMS alerts from Uptime Kuma and Grafana, sent from your own number
Checked against the docs:
In Uptime Kuma you add a Webhook notification: Post URL https://smsportal.app/api/v1/gateway/send-sms, Request Body set to Custom Body with JSON containing recipients and message, and Additional Headers with x-api-key and Content-Type: application/json. Grafana 12 and Healthchecks.io do the same with their own webhooks. The alert goes out as an SMS from your Android phone.
What you need
- Uptime Kuma 1.23 or newer (the Custom Body option in the Webhook notification).
- For Grafana: self-managed Grafana 12 or newer (Extra Headers and Custom Payload in the Webhook contact point).
- A smsportal account with an Android phone and SIM card connected, plus an API key (API Keys → Generate API Key).
- The monitoring tool runs outside the server it watches (otherwise it goes down with it).
How to send an SMS alert from Uptime Kuma
Create a Webhook notification
In Uptime Kuma open Settings → Notifications → Set Up Notification. Notification Type: Webhook, Friendly Name such as
SMS smsportal, Post URL:https://smsportal.app/api/v1/gateway/send-sms. If your version has an HTTP Method field, keep POST.Paste a custom body
Under Request Body choose Custom Body and paste the template. It builds a short
DOWN: name - reasonorUP: ...text, and thejsonfilter turns it into a valid JSON string, so a quote in a monitor name cannot break the request. The text is cut at 160 characters.Custom Body{% capture text %}{% if heartbeatJSON %}{% if heartbeatJSON.status == 0 %}DOWN{% else %}UP{% endif %}: {{ monitorJSON.name }} - {{ heartbeatJSON.msg }}{% else %}{{ msg }}{% endif %}{% endcapture %} { "recipients": ["+14155550123"], "message": {{ text | strip | truncate: 160 | json }} }Add the headers
Turn on Additional Headers and paste the JSON below.
Content-Typematters: Uptime Kuma sends a Custom Body as plain text without a JSON type, and without this header the API cannot readrecipientsandmessage.Additional Headers{ "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" }Test and assign to monitors
Click Test: you get the text
SMS smsportal Testing. Save. Default enabled turns the notification on for new monitors and Apply on all existing monitors for current ones. Set Retries to 1 or 2 on the monitor so a brief hiccup does not wake you at night.Grafana: Webhook contact point
In Grafana open Alerting → Contact points → Add contact point, Integration: Webhook, URL as above. Under Optional Webhook settings add an
x-api-keyheader with your key in Extra Headers, and paste the template into Payload Template under Custom Payload. Route alerts to it in Notification policies. On Grafana Cloud Custom Payload is not generally available yet: send the webhook to n8n and text from there.Grafana: Payload Template{{ coll.Dict "recipients" (coll.Slice "+14155550123") "message" (printf "Grafana %s: %s" (toUpper .Status) .CommonLabels.alertname) | data.ToJSON }}Healthchecks.io: Webhook integration
In your project open Integrations → Webhook. Under Execute when a check goes down pick POST, enter the API URL and fill in Request Body and Request Headers (one header per line). You can leave the up section empty.
$NAMEis the check name: keep quotes out of it, or the JSON breaks.Healthchecks.io: body and headersRequest Body: {"recipients": ["+14155550123"], "message": "Healthchecks: $NAME missed its check-in"} Request Headers: x-api-key: YOUR_API_KEY Content-Type: application/json
Ready-made scenarios
The shop or website stops responding
An HTTP monitor in Uptime Kuma checks the site every minute. After the failed retries you get a
DOWNtext, andUPonce it recovers. Run Uptime Kuma on a different server from the shop, such as a small VPS.Grafana: disk, 5xx errors, queue depth
A Grafana alert rule (for example free disk under 10%) sends a text with the alert name and
FIRINGorRESOLVED. If you do not want a text on resolve, turn on Disable resolved message in the contact point.The nightly backup never checked in
Your backup script pings its Healthchecks.io URL after a successful run. When the ping is late, Healthchecks.io sends the SMS. You find out in the morning, not on the day you need the backup.
Errors and fixes
- The test returns 401
- Cause: No
x-api-keyheader, a wrong key, or anAuthorizationheader instead ofx-api-key. - Fix: Check Additional Headers. If Uptime Kuma says
Additional Headers is not a valid JSON, fix the quotes and commas. - The test returns 400
- Cause: No
Content-Type: application/json, so the API sees no fields, or no phone is enabled in the dashboard. - Fix: Add
Content-Typeto Additional Headers (in Healthchecks.io to Request Headers). Check in the dashboard that the phone is enabled. - The test works but an outage sends nothing
- Cause: The notification is not ticked on the monitor.
- Fix: Edit the monitor and tick the notification under Notifications, or use Apply on all existing monitors.
- Grafana has no Custom Payload or Extra Headers
- Cause: Grafana older than 12, or Grafana Cloud, where Custom Payload is not generally available yet.
- Fix: Upgrade Grafana, or send the default webhook to n8n, take its
titleand build the SMS with an HTTP Request node. - The SMS arrives very late or not at all
- Cause: The gateway phone was offline. Messages wait in the queue for up to 72 hours, then expire.
- Fix: Keep the phone on a charger with battery optimisation off. Keep a second channel (email, Telegram) on the same monitor.
Frequently asked questions
Will the text arrive if my server or office internet is down?
Yes, as long as the monitoring tool runs elsewhere. The gateway phone is a separate device on mobile data, so a server or office line failure does not affect it. It still needs power and signal.
Does this replace a pager or on-call tool?
No. One phone is a single point of failure, with no escalation or acknowledgement. Treat SMS as one channel and add a second, such as email or Telegram.
How many texts will alerts use?
Each state change is one SMS per recipient (DOWN and UP make two). The Free plan has 300 SMS a month, Pro 3000. Set Retries so short blips do not eat the quota. The texts themselves are paid to your carrier through your SIM plan.
Can I alert several people?
Yes. Put several numbers in `recipients`, for example `["+14155550123", "+14155550124"]`. Each gets a separate SMS from your number.
Why does my text only fit 70 characters per part?
An emoji or a non-GSM character switches the encoding to UCS-2 and the limit drops from 160 to 70 characters per part. The template avoids emoji, but monitor names with such characters still switch the encoding.
Sources
- Uptime Kuma: Webhook notification source (GitHub)
- Grafana: Configure webhook notifications
- Healthchecks.io: Configuring Notifications
- Healthchecks.io: Webhook integration source (GitHub)
- smsportal API documentation
More in the guides
Send your first SMS today
Create a free account, connect your phone and see how customers respond to messages from your number.