Most workflow diagrams describe a process that starts when someone starts it and proceeds when someone advances it. Real processes are rarely that obedient. They wait for a payment to clear, a document to arrive, an upstream system to finish a nightly run, or a customer to reply to an email sent nine days ago.
Modelled badly, all of this becomes polling — a timer that wakes up every fifteen minutes and asks whether anything has happened yet. It works, it is easy to reason about, and it quietly becomes one of the largest sources of load and latency in the system.
IBM BAW has better primitives for this. They are worth knowing precisely.
Message events
A message event is a point in a process definition that waits for something external to arrive. It can start a process, or it can sit mid-flow as an intermediate catch, holding an instance until the awaited thing happens.
The important word is waits. An instance parked on a message event is not consuming a thread or running a query. It is dormant until something correlates to it.
Undercover agents
The message does not deliver itself. In BAW the mechanism that receives an external signal and directs it to the right place is an undercover agent — a UCA.
A UCA is attached to a message event and defines the payload the event accepts. Something outside fires it — typically via the REST surface, with the legacy family exposing ?action=sendMessage for exactly this — and BAW decides which instance the message belongs to.
Correlation is the whole design
That last step is where these projects succeed or fail.
When a message arrives, the engine must work out which of the eleven thousand waiting instances it is for. It does that by matching a correlation key in the message payload against a value the instance carries. Get this right and event-driven routing is close to free. Get it wrong and you have built something genuinely difficult to debug.
Three things reliably cause pain:
A key that is not unique. If two instances can carry the same correlation value, delivery becomes ambiguous, and the resulting behaviour will not be the one you wanted. Order number is usually safe; customer number usually is not, because a customer can have two things in flight.
A key that is not stable. If the value changes during the instance’s life — a reference reassigned during processing, say — messages sent after the change will not find it. Correlation keys must be immutable for the lifetime of the instance.
A key the sender does not have. The external system must be able to supply the value. An internal process ID the upstream system has never seen is not a correlation key, however unique it is. This is a design constraint on the whole integration, and it is best discovered before the build rather than during it.
What happens when the message never comes
Every wait needs an escape. The document may never arrive; the customer may never reply; the upstream system may have silently failed three days ago.
An instance waiting forever is invisible in exactly the wrong way: it is not an error, it does not raise an alert, it simply is not finished. Nobody notices until someone asks about a case from last quarter.
So every message wait should be paired with a timer boundary that eventually fires, and the timeout path should do something a human will see — escalate, notify, move to an exception queue. Choosing that duration is a business decision, not a technical one, and it belongs in discovery alongside the happy path.
How this fits interpreted flows
If your process definitions are application data rather than deployed artefacts, message events matter more, not less — because they are one of the small set of generic set pieces you deploy once and reuse everywhere.
The pattern is: a generic wait-for-event set piece, parameterised with an event name and a correlation key drawn from the run data. The flow’s author picks the event and the key in a configuration form; nothing is deployed. Routing stays a pure function of the graph, the current node, the arriving event and the run data.
The event bus is the seam. Node completions raise events on it; BAW callbacks and UCAs land on the same bus. The router does not care which side an event came from, which means the same routing logic can be exercised against a fake adapter long before anyone has resolved which BAW environment you are targeting.
The rule of thumb
If a step in your process is described with the word until — wait until the payment clears, hold until the documents arrive — that is a message event with a correlation key and a timeout. If you find yourself reaching for a fifteen-minute timer instead, you are polling, and you should at least be able to say why.
Choosing between a timer and an event
The decision is usually made by habit. It is worth making deliberately, because the two model different things.
A timer encodes time has passed. Use it when the passage of time is itself the business fact: a cooling-off period, a payment term, a service-level deadline.
An event encodes something happened elsewhere. Use it when the process is waiting on an external fact whose arrival time is not the point.
Polling — a timer that wakes up and checks — is the compromise, and it is occasionally the right one. It is defensible when the upstream system genuinely cannot notify you, when the check is cheap, and when the acceptable latency is much longer than the poll interval. It is indefensible as a default, and the tell is a poll interval far shorter than the business needs, chosen because someone was worried about latency and nobody measured.
Most real waits need both: an event for the expected case, and a timer alongside it for the case where the event never comes.
Designing the correlation key
Since correlation decides whether this works, it is worth a short checklist before build. A good key is:
- Unique across all instances that could be waiting simultaneously.
- Immutable for the life of the instance.
- Known to the sender — the external system must be able to supply it.
- Present at wait time — the instance must already hold it when it parks.
- Non-sensitive, since it travels through integration layers and logs.
When no single field satisfies all five, the usual fix is a composite key or a dedicated correlation identifier minted at the start of the process and passed outward with the request. The second is cleaner: it is yours, it is unique by construction, and it does not couple your routing to somebody else’s numbering scheme.
The reply that arrives before you are waiting
The race condition worth designing for explicitly. Your process calls an external system and then moves to a wait state — but the system replies fast enough that the message arrives before the instance is parked. Delivery fails, and the instance waits forever for something that already happened.
The mitigations are to park before making the outbound call, or to buffer inbound messages briefly and retry correlation. Which one is right depends on the engine’s semantics, but choosing neither means an intermittent, load-dependent failure that is genuinely unpleasant to diagnose.
Operating event-driven processes
Three things are worth monitoring from day one, because none of them raise errors on their own:
Instances waiting longer than expected. A wait state is not an error, so nothing alerts. A simple report of instances parked beyond their timeout window catches broken correlation, upstream failures and misconfigured keys — usually before the business notices.
Undelivered messages. Messages arriving that correlate to nothing. A steady trickle is normal (retries, duplicates). A spike means a key changed, an upstream system started sending a different identifier, or instances are being terminated while their counterparties still expect them.
Timeout-path volume. If the timeout branch fires more often than the happy path, the timeout is wrong, or the upstream integration is broken and the process has been quietly compensating for months.
EFTEDRA builds workflow automation on IBM Business Automation Workflow and Claude — assistant tasks and coach views that install into the processes you already run. See what we build, or try the live demo.

