While email notifications work well for standard office approvals, urgent alerts—such as appointment reminders, multi-factor OTP codes, or emergency escalations—require SMS messaging.
Joget DX enables developers to connect to any external SMS Gateway REST API (e.g., Twilio, Infobip, or local telecom providers) directly from a workflow BeanShell Tool step.
In this guide, we'll build a Java/BeanShell script that dynamically aggregates recipient phone numbers, constructs a bilingual SMS message, and dispatches a JSON POST payload to a REST SMS Gateway API.
How It Works
-
Phone Number Sanitization & Aggregation: The script retrieves recipient phone numbers from multiple form fields using Hash Variables (
#form.request.mobile_no#), strips empty entries, and formats a comma-separated list. - Supplemental Data Lookup: JDBC connection queries additional appointment details (e.g., meeting location or reference status) from internal Joget database tables.
-
HTTP REST Dispatch: Using
java.net.HttpURLConnection, the script sends a UTF-8 encoded JSON POST request to the Gateway URL configured in Joget App Variables (#appVariable.sms_gateway_url#).
The BeanShell Script
Place this code inside a BeanShell Tool step within your Joget workflow:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import org.json.JSONObject;
import org.joget.apps.app.service.AppUtil;
import org.joget.commons.util.LogUtil;
// 1. Collect and sanitize phone numbers from form fields
String primaryMobile = "#form.appointment.primary_phone#";
String secondaryMobile = "#form.appointment.secondary_phone#";
StringBuilder mobileBuilder = new StringBuilder();
if (primaryMobile != null && !primaryMobile.trim().isEmpty()) {
mobileBuilder.append(primaryMobile.trim());
}
if (secondaryMobile != null && !secondaryMobile.trim().isEmpty()) {
if (mobileBuilder.length() > 0) mobileBuilder.append(",");
mobileBuilder.append(secondaryMobile.trim());
}
String recipientMobiles = mobileBuilder.toString();
if (recipientMobiles.isEmpty()) {
LogUtil.warn("SMS Gateway", "No valid mobile numbers provided. Skipping SMS send.");
return;
}
// 2. Fetch supplemental data from database if needed
String processId = "#process.recordId#";
String location = "Main Headquarters";
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
javax.sql.DataSource ds = (javax.sql.DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
con = ds.getConnection();
String sql = "SELECT c_location FROM app_fd_appointment_details WHERE c_fk = ?";
ps = con.prepareStatement(sql);
ps.setString(1, processId);
rs = ps.executeQuery();
if (rs.next() && rs.getString("c_location") != null) {
location = rs.getString("c_location");
}
} catch (Exception e) {
LogUtil.error("SMS Gateway", e, "Error fetching appointment location");
} finally {
if (rs != null) try { rs.close(); } catch (Exception e) {}
if (ps != null) try { ps.close(); } catch (Exception e) {}
if (con != null) try { con.close(); } catch (Exception e) {}
}
// 3. Build SMS Body
String appointmentRef = "#form.appointment.ref_no#";
String appointmentDate = "#form.appointment.date#";
String messageBody = "Reminder: Your appointment (Ref: " + appointmentRef + ") is scheduled for " +
appointmentDate + " at " + location + ". Thank you.";
// 4. Send REST POST Payload to SMS Gateway
String smsGatewayUrl = "#appVariable.sms_gateway_endpoint#"; // e.g. https://api.sms-provider.com/v1/send
JSONObject jsonPayload = new JSONObject();
jsonPayload.put("recipients", recipientMobiles);
jsonPayload.put("message", messageBody);
try {
URL url = new URL(smsGatewayUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Authorization", "Bearer #appVariable.sms_api_key#");
conn.setDoOutput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
OutputStream os = conn.getOutputStream();
os.write(jsonPayload.toString().getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
int responseCode = conn.getResponseCode();
LogUtil.info("SMS Gateway", "SMS API Response Code: " + responseCode + " for recipients: " + recipientMobiles);
if (responseCode >= 200 && responseCode < 300) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String inputLine;
StringBuilder responseStr = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
responseStr.append(inputLine);
}
in.close();
LogUtil.info("SMS Gateway", "SMS Gateway Output: " + responseStr.toString());
} else {
LogUtil.error("SMS Gateway", null, "Failed to dispatch SMS. HTTP Response Code: " + responseCode);
}
} catch (Exception e) {
LogUtil.error("SMS Gateway", e, "Error dispatching HTTP POST request to SMS Gateway");
}
Best Practices
-
Timeout Configuration: Always set explicit connect (
setConnectTimeout) and read timeouts (setReadTimeout) so an unresponsive gateway doesn't lock up active workflow threads. -
Store Credentials Securely: Store API keys and endpoint URLs in App Variables (
#appVariable.sms_api_key#) rather than hardcoding them in scripts.
Top comments (0)