Hey everyone,
I have a question regarding the communication between a job worker and the service task (a.k.a flow node). The scenario is the following:
A long running 3rd party system (like a video transcoder) is managed by a job worker. Withing a workflow this job worker will be eventually activated via it’s corresponding service task within a workflow instance. This will start a long running video transcoding process. Let’s assume that the workflow instance is canceled by a user via Camunda Operate. The job worker will not be notified about this cancel action, thus the long running video transcoding process will proceed (and generate cost).
In order for the job worker to check the current state of the associated service task (a.k.a flow node) of the workflow instance (to then cancel the transcoding process) we would need to poll GET “Query flow node instances (alpha)” which is not an elegant solution because it would result in unnecessary load on the Camunda API.
Is there a way (or a plan) to introduce an asynchronous notification mechanism for the job worker to be informed about status changes of the associated service task (flow node) like SSE (server sent events) or Webhooks?
Hi there! 
Great question about job worker cancellation notifications! This is indeed a common challenge when dealing with long-running external processes.
Current State
Unfortunately, Camunda 8 does not provide built-in asynchronous notification mechanisms (like SSE or webhooks) specifically for job worker cancellations when workflow instances are cancelled. The standard job worker pattern is based on polling, and workers typically only discover cancellation when they try to complete the job and receive an error, or when the job times out.
Solutions Available
1. Custom Exporter (Self-Managed Only)
Recommended
If you’re using Camunda 8 Self-Managed, a custom exporter is the best solution for real-time cancellation notifications:
public class CustomExporterFilter implements RecordFilter {
@Override
public boolean acceptType(RecordType recordType) {
return recordType == RecordType.EVENT;
}
@Override
public boolean acceptValue(ValueType valueType) {
return valueType == ValueType.JOB || valueType == ValueType.WORKFLOW_INSTANCE;
}
@Override
public boolean acceptIntent(Intent intent) {
// Listen for job cancellations or workflow terminations
return intent == JobIntent.CANCELED || intent == WorkflowInstanceIntent.TERMINATED;
}
}
This allows you to:
- Capture job cancellation events in near real-time
- Trigger your own notification logic (webhooks, message queues, etc.)
- Notify your video transcoding service to stop processing
2. SaaS Limitations
For Camunda 8 SaaS, your options are more limited:
- Polling the API (as you mentioned) - not ideal due to load concerns
- Built-in alerting - only covers process errors/incidents, not regular cancellations
- Job timeout handling - design your job workers to handle timeouts gracefully
3. Alternative Patterns
Consider these architectural patterns:
- Shorter job timeouts with periodic “heartbeat” checks
- External state management where your transcoding service periodically checks a shared state store
- Message-based cancellation using intermediate message events
Best Practices
- Design for eventual consistency - external systems should handle the delay between cancellation and notification
- Implement proper cleanup - ensure your job workers can handle cancellation at any point
- Use job timeouts strategically - shorter timeouts mean faster detection of cancellations
Future Considerations
This is a legitimate feature gap that affects many users with long-running external processes. You might want to:
References:
Would you like me to help you explore any of these solutions in more detail, or do you have questions about implementing a custom exporter?
Maybe a workaround can do the job. You can’t detect the cancelation of a service task but you can detect the start of the next node after it:

You then add 3 Execution Listeners:
1 - Start of “Process Video” (job type = processVideoStart)
2 - End of “Process Video” (job type = processVideoEnd)
3 - Start of “Milestone” (job type = milestoneStart)
And you have the JobWorker for these listeners running in the same process of the worker that processes the video (job type = processVideo)
Normal executions will invoke the jobs in the following order:
processVideoStart
processVideo
processVideoEnd
milestoneStart
But if you cancel the task processVideo in Operate, moving the execution to Milestone, it will be:
processVideoStart
processVideo
milestoneStart
You just need to store each invoked job type for each process instance.
When the job milestoneStart is invoked, if there is an entry with processVideoEnd for the same process instance, it means that the task “Process Video” ended normally, otherwise it means it was cancelled.
You just need a way to identify which video processing thread to abort.
This could work. 
1 Like
That’s a brilliant workaround suggestion! 
Execution Listener Pattern for Cancellation Detection
This approach uses execution listeners to detect when a service task is bypassed due to cancellation. Here’s how it works:
Setup
Add three execution listeners to your process:
- Start of “Process Video” →
job type = processVideoStart
- End of “Process Video” →
job type = processVideoEnd
- Start of next node (e.g., “Milestone”) →
job type = milestoneStart
Detection Logic
Normal execution sequence:
processVideoStart → processVideo → processVideoEnd → milestoneStart
Cancelled execution sequence:
processVideoStart → processVideo → milestoneStart (missing processVideoEnd!)
Implementation Strategy
- State tracking: Store job invocations per process instance (in-memory map, database, etc.)
- Cancellation detection: When
milestoneStart
fires, check if processVideoEnd
was recorded
- Cleanup trigger: If
processVideoEnd
is missing, cancel the video transcoding process
Sample Job Worker Logic
// Track job states per process instance
Map<String, Set<String>> processStates = new ConcurrentHashMap<>();
@JobWorker(type = "milestoneStart")
public void handleMilestone(ActivatedJob job) {
String processInstanceKey = job.getProcessInstanceKey();
Set<String> completedJobs = processStates.get(processInstanceKey);
if (completedJobs == null || !completedJobs.contains("processVideoEnd")) {
// Video processing was cancelled - trigger cleanup
cancelVideoTranscoding(processInstanceKey);
}
// Complete the milestone job
jobClient.newCompleteCommand(job.getKey()).send();
}
Advantages of This Approach
Works in both SaaS and Self-Managed
No API polling required
Near real-time detection
Uses standard BPMN constructs
Reliable and deterministic
Considerations
- Process design: Requires adding execution listeners to your model
- State management: Need to track job states and clean up completed processes
- Thread identification: As mentioned, you’ll need a way to map process instances to transcoding threads
This is actually a very elegant solution that leverages BPMN’s execution semantics! It’s much better than polling and should work reliably for your video transcoding use case.
Have you considered this pattern before, or would you like help implementing any specific part of it?
1 Like