SMSTunnel for Drupal
Texts from your Drupal site, from your own phone and number
Texts from a number people know
Your Drupal site sends texts from your own Android phone, from your own number. Get a text for every new form submission, text a confirmation to the person who wrote, or send sign-in codes. People see a number they know and can reply to it.
What you can do
Webform: text on submission
Add the "Text on submission" handler to any webform. You get a text when someone submits it, and the person who filled it in can get a confirmation on the number they typed.
ECA: a text from any automation
The "Send a text with SMSTunnel" action works in every ECA model: new content, new users, cron, form submissions, Drupal Commerce orders.
SMS Framework: your phone as the provider
Everything built on SMS Framework, such as phone number verification for user accounts, sends through your own phone.
Choose who gets the text, what it says, and when.
Why your own phone
- A number people know. Texts come from your number, so people trust them and can reply.
- No sender registration, no new number. Your phone and SIM do the sending.
- You stay in control. Delete the site's key in your dashboard and the site can no longer send.
- Privacy built in. The module logs each text with the number partly hidden and never logs what the text says. The key stays out of configuration exports.
Coming soon.
Want it early? Write to us.
Until then: the REST API
A small custom module can call SMSTunnel's API with Drupal's own HTTP client (the
http_client service): one HTTPS request per text, with your key in the
X-API-Key header. The API reference
has every endpoint, and the prompt below writes the module with you.
Questions
- Which Drupal versions?
- Drupal 10.3 or newer and Drupal 11, PHP 8.1+. Tested with Webform 6.3, ECA 2.1 and 3.1, SMS Framework 2.3 and 3.0.
- Where do I keep the API key?
-
Best with the Key module (an environment variable or a file outside the web root) or in
settings.php. A key pasted on the settings page is never exported with the configuration. - What if my phone is offline?
- The text waits on your account and leaves as soon as the phone is back online.
- What if SMSTunnel can't be reached?
- The form or automation carries on; the error is logged and shown on the status report.
- Can it text several people?
- Each number gets its own text, one at a time. Send only texts people expect from you: alerts to yourself, confirmations, reminders and sign-in codes.
- Sign-in codes?
-
Yes, from ECA or your own code: the text must contain the code as 4 to 10 digits (
482913or123-456). Codes have their own quota.
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 make my Drupal 10.3+ or 11 site send texts with SMSTunnel's REST API, from a small custom module.
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:
- Write a small custom module with a service, for example a text sender with a send($to, $text) method, that calls the API with Drupal's own HTTP client (the http_client service), with a timeout.
- Read the key from settings.php (for example from getenv('SMSTUNNEL_API_KEY')) or the Key module, never from configuration that gets exported.
- Call it where my site needs it (a form submit handler, an entity hook, cron), one call per number. Log failures without the words of the text, and never let a failed text break a page or a form.
- 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.
SMSTunnel API facts:
- Send a text: POST https://smstunnel.io/api/v1/sms/send with the headers "X-API-Key: <key>" and "Content-Type: application/json", and the JSON body {"to": "+14155550123", "message": "..."}. Optional: "deviceId" (which of my phones sends it; default: my main phone) and "defaultCountryCode". Other fields are refused with 400.
- Call it from a server: browsers can't send the X-API-Key header to SMSTunnel (CORS), and the key must stay secret.
- One recipient per call. To text several people, make one call per number.
- Numbers: international format, with + and the country code. For numbers written without + or 00, "defaultCountryCode" ("44", "+44" or "0044") completes them, and it always takes precedence over the country set for the phone.
- A text can be up to 1600 characters (10 joined SMS).
- Sign-in codes: POST https://smstunnel.io/api/v1/sms/send-2fa, same headers and fields. The text must contain the code as 4 to 10 digits standing on their own (like 482913) or as 123-456, and can be up to 320 characters; otherwise the answer is 400 "No valid verification code found". Codes have their own daily allowance. My app creates and checks the code; SMSTunnel only delivers the text.
- Success: HTTP 200 {"success": true, "messageId": "...", "data": {"messageId": "...", "branded": false, ...}}. "queued": true is added when the phone is offline: the text waits and goes out as soon as the phone is back (for up to 3 days). On the Free plan, 25 texts a day (500 a month) go out as written, up to 25 more a day go out with a short "free by smstunnel.io" line added ("branded": true), and past that the answer is 429.
- Errors: 400 bad input (the "message" field says why, sometimes as a list); 401 missing or invalid key; 403 the key lacks a permission ("code": "PERMISSION_REQUIRED", "permission": "...") or my plan does not include the feature; 429 with a Retry-After header: too many requests (30 send requests a minute per API key, 90 for all keys of one account together), so wait that many seconds, then retry; 429 without Retry-After: the plan's limit for the day or month is reached, so don't retry before then; 5xx: retry later, with a limit.
- Safe retries: send the header "Idempotency-Key: <one key per text>" (1 to 255 printable ASCII characters, such as order-1042-shipped or a UUID stored with the text). A repeat with the same key within 24 hours gets the first answer back (or the same 5xx error) instead of a second text; 409 while the first request is still running, 422 if the key was used for a different text.
- Status: GET https://smstunnel.io/api/v1/sms/status/{messageId} answers {"success": true, "data": {"status": ...}}, with pending, sent, delivered or failed. If "unconfirmed" is true, the phone handed the text to its SIM without getting a confirmation: it was probably sent, so never resend it automatically.
Rules for your answer:
- Keep the API key in an environment variable or the platform's secret store (for example SMSTUNNEL_API_KEY). Never put it in source code, in a repository, or in code that runs in a browser or a mobile app: call SMSTunnel from a server. I will not paste my key in this chat; use a placeholder.
- Validate phone numbers before sending (international format, like +14155550123) and skip or report the invalid ones.
- Handle errors: log or show what went wrong, retry only after 429, 5xx or network errors and only a few times, and never send the same text twice.
- Only send texts people expect from me: alerts to myself, reminders, sign-in codes, updates they asked for. No unsolicited texts. 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.
Drupal is a registered trademark of Dries Buytaert. SMSTunnel is a third-party integration and is not affiliated with, endorsed by, or sponsored by Dries Buytaert, the Drupal Association, or the maintainers of Webform, ECA, SMS Framework or Key.