When integrating Joget DX with external platforms—such as payment gateways, SMS providers, or ERP systems—requests are often processed asynchronously. The external system accepts a request immediately and dispatches an HTTP POST webhook callback to Joget minutes or hours later when processing completes.
Receiving webhook callbacks inside BeanShell API endpoints requires two key tasks:
-
Safe Variable Type Coercion: Handling parameter arrays (
String[]) versus single strings (String) safely without throwingClassCastException. -
FormDataDao Persistence: Saving or updating the notification payload inside a Joget form database table using
FormDataDao.
In this guide, we'll write a defensive Java/BeanShell script that receives asynchronous webhook callbacks and logs them cleanly into Joget.
Architecture Overview
-
Webhook Endpoint: An external system hits your Joget API endpoint with callback parameters (e.g.
process_id,status,response_payload,recipient). - Type Extraction: A safe helper function handles parameter type variations (whether passed via URL query params or JSON request bodies).
-
FormDataDao Save: Instead of executing raw JDBC queries, the script uses
FormDataDaoto persist aFormRowSetdirectly into Joget's form storage engine.
The BeanShell Script
Place this code inside your API Builder BeanShell script or custom REST endpoint:
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.form.dao.FormDataDao;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.commons.util.LogUtil;
import java.util.UUID;
// 1. Safe Type Extraction Helper
public String safeExtract(Object param) {
if (param == null) return "";
try {
if (param instanceof String[]) {
String[] arr = (String[]) param;
return arr.length > 0 ? arr[0] : "";
}
if (param instanceof String) {
return (String) param;
}
} catch (Throwable t) {
LogUtil.error("Webhook Receiver", t, "Error extracting parameter");
}
return "";
}
// 2. Extract Webhook Callback Parameters Safely
String processId = safeExtract(request.getParameter("process_id"));
String recordId = safeExtract(request.getParameter("record_id"));
String statusValue = safeExtract(request.getParameter("status"));
String recipient = safeExtract(request.getParameter("recipient"));
String responseMsg = safeExtract(request.getParameter("response_message"));
String moduleName = safeExtract(request.getParameter("module"));
LogUtil.info("Webhook Receiver", "Received callback for processId: " + processId + " | Status: " + statusValue);
// 3. Persist Log using Joget FormDataDao API
try {
FormDataDao formDataDao = (FormDataDao) AppUtil.getApplicationContext().getBean("formDataDao");
String formDefId = "notification_log"; // Joget Form ID
String tableName = "app_fd_notification_log"; // Database Table Name
FormRow row = new FormRow();
row.setId(UUID.randomUUID().toString()); // Generate primary key
row.setProperty("c_process_id", processId);
row.setProperty("c_record_id", recordId);
row.setProperty("c_status", statusValue);
row.setProperty("c_recipient", recipient);
row.setProperty("c_response_message", responseMsg);
row.setProperty("c_module", moduleName);
FormRowSet rowSet = new FormRowSet();
rowSet.add(row);
// Save record to Joget database
formDataDao.saveOrUpdate(formDefId, tableName, rowSet);
LogUtil.info("Webhook Receiver", "Successfully saved callback notification log.");
} catch (Exception e) {
LogUtil.error("Webhook Receiver", e, "Error persisting webhook callback log");
}
Best Practices
-
Use FormDataDao for Integrity: Utilizing
FormDataDao.saveOrUpdate()automatically updates Joget's internal metadata columns (dateCreated,dateModified) and triggers active plugins. -
Always Validate Inputs: Ensure
process_idorrecord_idis validated to prevent invalid records from polluting your notification logs.
Top comments (0)