SMSTunnel for Gatus

Health check alerts by text, from your own phone

Built into Gatus · no plugin
Custom alerting provider
Gatus docs

Know when an endpoint goes down

When an endpoint fails its conditions a few times in a row, Gatus hands the alert to your SMSTunnel account and your own Android phone texts it to you from your own number, with the error Gatus saw. When it's healthy again, you get a second text.

1. Get your key

  1. 1. Install the SMSTunnel app from Google Play on your Android phone and sign in.
  2. 2. In your dashboard, open Apps → Add an app, pick Script or server, name it (for example "Gatus") and click Create the key.
  3. 3. Copy the key right away: it's shown only once. You can stop or delete the app there anytime, and Gatus can't send texts anymore.

Get the steps with your key in them

It opens Add an app with Script or server picked. Once the key is made, the dashboard shows every step and command with the key already filled in, ready to copy.

2. Add the custom provider

In Gatus's config.yaml, add alerting.custom and an alert of type custom on each endpoint that should text you:

config/config.yaml: your number, in international format
alerting:
  custom:
    url: "https://smstunnel.io/api/v1/sms/send"
    method: "POST"
    headers:
      Content-Type: "application/json"
      X-API-Key: "${SMSTUNNEL_API_KEY}"
    body: |
      {
        "to": "+14155550123",
        "message": "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION] - [RESULT_ERRORS]"
      }

endpoints:
  - name: shop
    url: "https://shop.example.com/health"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: custom
        failure-threshold: 3
        success-threshold: 2
        send-on-resolved: true
        description: "shop is down"
  • Keep the Content-Type header: Gatus doesn't send one of its own, and without it SMSTunnel can't read the body.
  • ${SMSTUNNEL_API_KEY} comes from the environment, so the key stays out of the file. Start Gatus with it:
docker run -d --name gatus -p 8080:8080 \
  -e SMSTUNNEL_API_KEY=sk_live_... \
  -v "$PWD/config:/config" twinproduction/gatus

To try it, point an endpoint at an address that doesn't answer and set failure-threshold: 1: the text arrives after the first failed check.

What the texts look like

TRIGGERED: shop - shop is down - Get "https://shop.example.com/health": dial tcp 203.0.113.7:443: connect: connection refused
RESOLVED: shop - shop is down - 

Without a key: a webhook address

A webhook address is a secret URL that turns each call into a text to the numbers you save with it, sent by your own phone from your own number. The call only sets the words, never the number, so Gatus needs no key.

  1. 1. Install the SMSTunnel app from Google Play on your Android phone and sign in.
  2. 2. In your dashboard, open Apps → Webhooks, click New webhook and pick Gatus. That fills in the name. Leave Text of the message empty, so the words come from each call.
  3. 3. Add the numbers that should get the texts, with + and the country code: up to 10, and each one gets its own text. Then click Create the webhook.
  4. 4. Copy the address, which looks like https://smstunnel.io/api/v1/webhook/<token>. Keep it secret, like a password: whoever has it can text these numbers from your phone, though never any other number.

Get the steps with your address in them

It opens New webhook with Gatus picked. Once the webhook is made, the dashboard shows its address and the Gatus steps with the address already in them, ready to copy, and Send a test, which texts the webhook's numbers.

  • Calls an hour: 20 unless you set another number, up to 500. The limit counts calls, not texts: each call texts every number of the webhook, and a test from the dashboard counts too. Past the limit, calls are refused until the hour is over, so a storm of alerts can't use up your texts.
  • You can copy the address again anytime, under Address and setup. If it gets out, New address in the webhook's menu replaces it, and the old one stops working at once.
  • A GET with ?message=… or a POST with JSON sets the words: its message, text, title or body field. Texts are cut at 480 characters.
  • Or with the API, for scripts: POST /api/v1/webhooks with your sign-in token, as the webhooks guide shows.

Then, in Gatus

In Gatus's config.yaml, add this alerting.custom section with the webhook's address, and an alert of type custom on each endpoint that should text you:

config/config.yaml: your webhook's address
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]"}

endpoints:
  - name: shop
    url: "https://shop.example.com/health"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: custom
        failure-threshold: 3
        send-on-resolved: true
        description: "shop is down"
  • Keep the Content-Type line: Gatus doesn't send one of its own, and without it SMSTunnel can't read the body. The body needs only message: the numbers are the webhook's.
  • Restart Gatus. To try it, point an endpoint at an address that doesn't answer, with failure-threshold: 1.
What the texts look like
TRIGGERED: shop - shop is down
RESOLVED: shop - shop is down

Several people

A webhook address texts up to 10 numbers saved with it, each its own text. With a key, SMSTunnel sends one text to one number per request: for a second number, add a second alert of type custom to the endpoint and give it its own body:

    alerts:
      - type: custom
        description: "shop is down"
      - type: custom
        description: "shop is down"
        provider-override:
          body: |
            {
              "to": "+14155550124",
              "message": "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION]"
            }

For whole groups of endpoints, alerting.custom.overrides sets a different body per group.

Worth knowing

  • No double quotes in names and descriptions. Gatus pastes [ENDPOINT_NAME] and [ALERT_DESCRIPTION] into the body as they are, and a quote would break the JSON. [RESULT_ERRORS] is safe: Gatus escapes its quotes.
  • Thresholds decide the texts. failure-threshold failed checks in a row send one; send-on-resolved sends one when it's back. Gatus doesn't repeat a text unless you set minimum-reminder-interval (5 minutes or more).
  • 30 sends a minute, up to 1600 characters per text. A webhook address cuts texts at 480 characters and takes 20 calls an hour unless you raise it.
  • Phone offline? The text waits and goes out as soon as the phone is back.

If it doesn't work

  • Gatus logs each alert it sends. Run docker logs gatus and look for Sending custom alert and any error after it.
  • 401 "Invalid or expired API key": the key is wrong or was deleted. "API key is required. Use X-API-Key header.": the key isn't in an X-API-Key header. SMSTunnel reads the key only from that header: Bearer or Basic authentication won't work.
  • 403 with PERMISSION_REQUIRED: the key may not send texts. Make a new one under Apps → Add an app.
  • 400 "property … should not exist": the body has a field SMSTunnel doesn't know. Send only to and message (and, if you need them, deviceId and defaultCountryCode). "message must be shorter than or equal to 1600 characters": shorten the text. "Phone number must contain…": put the number in international format, like +14155550123.
  • 400 with "to must be a string" and "message must be a string": the body didn't arrive as JSON. Add the header Content-Type: application/json.
  • 404 "Cannot GET /api/v1/sms/send": the tool sent a GET. Choose POST.
  • 404 "Webhook not found or disabled": the address is wrong, was replaced with New address, or the webhook is stopped or deleted. Copy it again from Apps → Webhooks → Address and setup.
  • 400 "Could not build a message from the payload": the call had no words in it. Add ?message=… to a GET, or a message field to the JSON. With a text of the message, check that the names between braces are the call's.
  • 429: too many texts at once. SMSTunnel takes 30 send requests a minute. Wait a minute and try again.
  • 429 "Rate limit reached for this webhook (20/hour)": the webhook took all its calls for this hour. Wait, or raise Calls an hour (up to 500) with Edit in Apps → Webhooks. It counts calls, not texts: each call texts every number of the webhook.
  • In Apps → Webhooks, the webhook's row shows when it was last used and, if its last call sent no text, why.
  • No text, but no error? If your phone is offline, the text waits and goes out as soon as the phone is back online. Open the SMSTunnel app on the phone to check it's linked.

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 get Gatus alerts as text messages from my own phone, with SMSTunnel.

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:
- No plugin: Gatus's "custom" alerting provider calls SMSTunnel, and each endpoint gets an alert of type custom. Two ways: a webhook address (no key; it texts up to 10 numbers saved with it) or SMSTunnel's API with a key.
- Webhook address way, config.yaml (the address made in my dashboard with Gatus picked; keep the Content-Type line, Gatus sends none of its own):
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]"}

endpoints:
  - name: shop
    url: "https://shop.example.com/health"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: custom
        failure-threshold: 3
        send-on-resolved: true
        description: "shop is down"
- Without a key: an SMSTunnel webhook address, https://smstunnel.io/api/v1/webhook/<token>. I make it in my dashboard (https://dashboard.smstunnel.io): Apps → Webhooks → New webhook, pick the tool that will call it (Grafana, Uptime Kuma, Healthchecks, Gatus, Alertmanager, Odoo Online, or Something else), add 1 to 10 numbers (each gets its own text), optionally a text of the message that pulls fields out of the JSON with {{path}}, like {{alarm.name}}, then Create the webhook and copy the address. A GET with ?message=… or a POST with JSON (its message, text, title or body field) texts those numbers; the call never sets the number. Texts are cut at 480 characters. "Calls an hour" (20 unless I set another number, up to 500) counts calls, not texts: each call texts every number, and calls past it are refused until the hour is over. The address can be copied again under Address and setup; New address replaces it if it leaks. Scripts can make one with POST https://smstunnel.io/api/v1/webhooks and my sign-in token instead.
- API key way, config.yaml:
alerting:
  custom:
    url: "https://smstunnel.io/api/v1/sms/send"
    method: "POST"
    headers:
      Content-Type: "application/json"
      X-API-Key: "${SMSTUNNEL_API_KEY}"
    body: |
      {
        "to": "+14155550123",
        "message": "[ALERT_TRIGGERED_OR_RESOLVED]: [ENDPOINT_NAME] - [ALERT_DESCRIPTION] - [RESULT_ERRORS]"
      }

endpoints:
  - name: shop
    url: "https://shop.example.com/health"
    interval: 1m
    conditions:
      - "[STATUS] == 200"
    alerts:
      - type: custom
        failure-threshold: 3
        success-threshold: 2
        send-on-resolved: true
        description: "shop is down"
- Gatus sends no Content-Type of its own, so the header above is needed. ${SMSTUNNEL_API_KEY} is read from the environment (a literal $ is written $$).
- Placeholders: [ALERT_TRIGGERED_OR_RESOLVED], [ENDPOINT_NAME], [ENDPOINT_GROUP], [ENDPOINT_URL], [ALERT_DESCRIPTION], [RESULT_ERRORS], [RESULT_CONDITIONS]. Only [RESULT_ERRORS] has its quotes escaped, so endpoint names and alert descriptions must not contain double quotes.
- One recipient per request: SMSTunnel sends one text to one number. For several people, one request (or one notification) per number. In Gatus, add a second alert of type custom with provider-override.body for each extra number, or use alerting.custom.overrides per endpoint group.
- failure-threshold / success-threshold decide when it texts; send-on-resolved texts when it is back; minimum-reminder-interval (5m or more) resends while it stays down.
- Answers: 200 {"success": true, "messageId": "…"}; 401 wrong or deleted key; 403 the key lacks a permission ("code": "PERMISSION_REQUIRED"); 400 a missing, unknown or too long field; 429 too many requests (30 sends a minute, with Retry-After): wait and retry; without Retry-After, the plan's limit for the day or month is reached. If my phone is offline the text waits and goes out when it is back, within 3 days.
- The API key comes from my SMSTunnel dashboard (https://dashboard.smstunnel.io): Apps → Add an app → Create the key. It is shown only once and can send texts and check whether they went out.

Rules for your answer:
- Do not ask for my SMSTunnel password or API key and do not put a real key in your answer; I will not paste it in this chat.
- Phone numbers must be in international format (+ and the country code).
- Help me test it with my own number first.
- Only send texts people expect from me. No unsolicited texts.
- Help me pick thresholds so a short blip does not send a text.

Responsible use

Built for alerts to yourself and your team, and texts people expect from you. One text goes to one number. Sending unsolicited messages from a consumer SIM generally breaches mobile carrier terms of service.

Gatus is an open-source project by TwiN. SMSTunnel is a third-party integration and is not affiliated with, endorsed by, or sponsored by the Gatus project.

No phone paired yet?

Install the Android app and sign in with your account, and you're ready in about a minute.

Get it on Google Play