Completing a User Task from Email

Greetings,

Is it possible to complete a user task from the email?

Goal: making the email that was sent in the “Send request” step determine the flow of the process.

I couldn’t find or think of how this would be done directly from the email, so I had to replace the user task with a service task (to be specific: an execution listener of type ReceiveAnswer attached to the gateway):

  • The implementation of ReceiveAnswer is using Camunda 8 REST API to activate and complete the job.
  • The email invokes the implementation of ReceiveAnswer through one of the app’s endpoints. (Each button sends a request to that endpoint with the answer.)

image

This is working fine, but I was wondering if there is a better solution to this case?
For example, sending a public link of the user task in the email so that the task can be completed without the need to send a request to the application.

Currently, I’m trying this on SaaS, but eventually I will make it self-deployed.

Thank you! :pray:

Hi @MrDeeb In Camunda 8, user tasks are generally intended for human interaction through Tasklist or custom UIs. However, if your goal is to complete a user task based on user input via email, you can implement this using a combination of:

  1. Email receiving service (e.g., IMAP or webhook)
  2. A Java worker or external service to complete the user task
  3. Camunda 8 APIs to correlate and complete the task

Here’s a high-level approach to implementing this:

1. Design the BPMN model

  • Add a User Task.
  • Give it a unique taskDefinitionKey (important for querying).
  • Optionally follow it with a Service Task or Intermediate Message Catch Event to proceed based on email confirmation.

2. Send an email for user input

In a Service Task before the user task:

  • Send an email using Java Mail or any SMTP client.
  • Include a unique identifier (like a business key or task ID) and instructions (e.g., “Reply with YES to approve”).

3. Set up an Email Listener

Use Java (or Node.js, Python, etc.) to monitor an inbox:

  • Poll the inbox via IMAP, or use a webhook with a mail service like SendGrid or Mailgun.
  • Parse incoming messages.
  • Match the message to a process instance using the business key or task metadata.

4. Find and Complete the User Task

Camunda 8 user tasks are managed via Tasklist API or Operate API (currently read-only). To proUse the Tasklist GraphQL API:

  • Query for the task using taskDefinitionKey and processInstanceId or businessKey
  • Use the tasklist api to complete the task.

Or, if you’re okay switching from a user task to a message event, you can:

  • Replace the user task with a Message Intermediate Catch Event.
  • Send a message correlation from your email listener to proceed with the process.

5. Security & Validation

Make sure to:

  • Validate sender email
  • Avoid email spoofing
  • Handle timeouts if users don’t respond
1 Like

@MrDeeb Camunda provides the Tasklist REST API in self-managed setups starting in recent versions. Here’s how you can use it to complete user tasks using REST:

1. Enable Tasklist REST API

In self-managed Camunda 8, the REST API is experimental. Make sure it’s enabled in your application.yaml or environment config.

Example config:

camunda.tasklist.rest-enabled: true

Or as an env variable:

CAMUNDA_TASKLIST_REST_ENABLED=true

2. REST Endpoint to Complete a Task

Once enabled, you can use:

POST /tasklist/v1/tasks/{taskId}/complete

Request Body:

{
  "variables": {
    "approval": "yes"
  }
}

3. Java Example Using REST (with OkHttp)

import okhttp3.*;

public class CompleteUserTask {
    public static void main(String[] args) throws Exception {
        String taskId = "YOUR_TASK_ID"; // Replace with actual task ID
        String url = "http://localhost:8080/tasklist/v1/tasks/" + taskId + "/complete";
        String bearerToken = "YOUR_AUTH_TOKEN"; // If authentication is enabled

        String jsonBody = "{ \"variables\": { \"approval\": \"yes\" } }";

        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(
            jsonBody,
            MediaType.parse("application/json")
        );

        Request.Builder requestBuilder = new Request.Builder()
            .url(url)
            .post(body)
            .addHeader("Content-Type", "application/json");

        if (!bearerToken.isEmpty()) {
            requestBuilder.addHeader("Authorization", "Bearer " + bearerToken);
        }

        try (Response response = client.newCall(requestBuilder.build()).execute()) {
            if (response.isSuccessful()) {
                System.out.println("Task completed successfully.");
            } else {
                System.err.println("Error completing task: " + response.code() + " - " + response.body().string());
            }
        }
    }
}

4. Get Tasks via REST

You can list tasks assigned to a user or available with this endpoint:

GET /tasklist/v1/tasks

Optional query params: state=CREATED, taskDefinitionId, processInstanceId, assignee, etc.

5. Authentication Note

If you’re using Camunda Identity, you’ll need to:
• Authenticate via Keycloak
• Use the OAuth Bearer token in your request headers

1 Like

Hi @aravindhrs
Thank you so much for the detailed answer!

Is the endpoint: GET /tasklist/v1/tasks documented somewhere?
The last time I tried to get a specific user task to complete, I used POST /tasklist/v1/tasks/search from Search tasks, and it was hard to filter tasks with this one.

Thanks again for your ideas and the high-level approach you provided.

1 Like

Yes. That’s correct. The endpoint is: /v1/tasks/search

1 Like

This api is used to send request payload to search and filter tasks:

curl -L 'http://localhost:8080/v1/tasks/search' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TASKLIST-SESSION>' \
-d '{
  "state": "CREATED",
  "assigned": true,
  "assignee": "string",
  "assignees": [
    "string"
  ],
  "taskDefinitionId": "string",
  "candidateGroup": "string",
  "candidateGroups": [
    "string"
  ],
  "candidateUser": "string",
  "candidateUsers": [
    "string"
  ],
  "processDefinitionKey": "string",
  "processInstanceKey": "string",
  "pageSize": 0,
  "followUpDate": {
    "from": "2024-07-29T15:51:28.071Z",
    "to": "2024-07-29T15:51:28.071Z"
  },
  "dueDate": {
    "from": "2024-07-29T15:51:28.071Z",
    "to": "2024-07-29T15:51:28.071Z"
  },
  "taskVariables": [
    {
      "name": "string",
      "value": "string",
      "operator": "eq"
    }
  ],
  "tenantIds": [
    "string"
  ],
  "sort": [
    {
      "field": "completionTime",
      "order": "ASC"
    }
  ],
  "searchAfter": [
    "string"
  ],
  "searchAfterOrEqual": [
    "string"
  ],
  "searchBefore": [
    "string"
  ],
  "searchBeforeOrEqual": [
    "string"
  ],
  "includeVariables": [
    {
      "name": "string",
      "alwaysReturnFullValue": false
    }
  ],
  "implementation": "JOB_WORKER",
  "priority": {
    "eq": 0,
    "gte": 0,
    "gt": 0,
    "lt": 0,
    "lte": 0
  }
}
1 Like

This api GET: /v1/tasks/:taskId is just pulls the task for the given taskid.

1 Like

This isn’t good style - it actually hides a portion of the business logic / business workflow from from the reader/modeler.

After your Send Request, model a “Receive Reply” and then your gateway is purely evaluating what the reply was.
The “Receive Reply” can be REST activated, it could be Webhook, could be a lot of other things… I would probably use an external webpage to allow both the Yes and No buttons in the email (note that this is becoming a bad practice from a security perspective) to send a response back the process. The URLs behind these buttons in the email can include the process ID making it trivial to correlate.

You could potentially even set up IMAP mailbox, allowing the parsing of a reply to the email (eg. Please reply YES as the body of this email to accept the provisioning request).

1 Like

Thanks @GotnOGuts for pointing this out. I will modify it to be an explicit step.