BPM / BAW has no built-in mail listener; inbound e-mail integration is done with a small poller that turns mails into BPM events. Three implementations, from lightest to most robust:
- Scheduled UCA + Jakarta Mail (IMAP): a UCA on a schedule (every 2 minutes) runs a service flow with a Java integration that connects to the mailbox (IMAP over TLS), reads unseen messages, extracts subject / body / attachments, stores attachments in the document store, and starts a process (or sends a message event) per mail; marks the mail as seen or moves it to a folder.
- Mail server rule + REST: the mail system (Exchange transport rule, Postfix alias to a script, Microsoft Graph subscription) posts the mail as JSON to a BPM REST start endpoint or drops it on a queue consumed by a UCA - BPM stays passive; better security posture (no mailbox credentials in BPM).
- Content-based: mails are filed into FileNet (IBM Content Collector / Datacap) and a document start event starts the process with the document - the pattern for regulated inbound handling.
// Java integration (Jakarta Mail, IMAP): returns a JSON array of new mails; attachments as Base64 for the document store
public static String fetch(String host, String user, String pw, String folderName) throws Exception {
Properties p = new Properties(); p.put("mail.store.protocol", "imaps");
Store store = Session.getInstance(p).getStore(); store.connect(host, user, pw);
Folder folder = store.getFolder(folderName); folder.open(Folder.READ_WRITE);
Message[] msgs = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
JSONArray out = new JSONArray();
for (Message m : msgs) {
JSONObject j = new JSONObject(); j.put("from", InternetAddress.toString(m.getFrom())); j.put("subject", m.getSubject()); j.put("date", m.getSentDate().toInstant().toString());
JSONArray atts = new JSONArray(); String body = "";
if (m.isMimeType("multipart/*")) { Multipart mp = (Multipart) m.getContent();
for (int i = 0; i < mp.getCount(); i++) { BodyPart bp = mp.getBodyPart(i);
if (Part.ATTACHMENT.equalsIgnoreCase(bp.getDisposition())) { JSONObject a = new JSONObject(); a.put("name", bp.getFileName()); a.put("type", bp.getContentType()); a.put("base64", Base64.getEncoder().encodeToString(bp.getInputStream().readAllBytes())); atts.put(a); }
else if (bp.isMimeType("text/*") && body.isEmpty()) body = (String) bp.getContent(); } }
else body = String.valueOf(m.getContent());
j.put("body", body); j.put("attachments", atts); out.put(j); m.setFlag(Flags.Flag.SEEN, true);
}
folder.close(false); store.close(); return out.toString();
}// UCA service flow script after the Java step
var mails = JSON.parse(tw.local.json);
for (var i = 0; i < mails.length; i++) {
var m = mails[i], input = new tw.object.Map(); input.put("subject", m.subject); input.put("from", m.from); input.put("body", m.body);
var docIds = new tw.object.listOf.String();
for (var k = 0; k < m.attachments.length; k++) docIds.insertIntoList(k, tw.system.createDocument(m.attachments[k].name, m.attachments[k].type, m.attachments[k].base64, true).id);
input.put("documentIds", docIds);
tw.system.startProcessByName("Handle inbound mail", input); // one instance per mail; or correlate to a waiting instance via sendMessage
}Operational points: dedicated mailbox, app password / OAuth2 (Microsoft 365 needs OAuth2 for IMAP now - use the Graph API variant instead), idempotency (message-id stored to avoid duplicates), size limits for attachments, and monitoring of the UCA (a mail that fails parsing must not block the queue - catch per message).
References